将上传的文件保存在servlet应用程序中的推荐方法
我在这里读到,不应该将文件保存在服务器中,因为它不是可移植的,事务性的并且需要外部参数。 但是,鉴于我需要一个tomcat(7)的tmp解决scheme,并且我有(相对)对服务器机器的控制,我想知道:
-
什么是保存文件的最佳地点? 我应该将其保存在
/WEB-INF/uploads
(在这里build议)还是在$CATALINA_BASE
(见这里 )或…下的某个地方? JavaEE 6教程从用户 (:wtf 🙂 获取path 。 注:文件不得以任何方式下载。 -
我应该如何设置一个configuration参数? 我会欣赏一些代码(我宁愿给它一个相对path – 所以它至less是Tomcat便携式) –
Part.write()
看起来很有前途 – 但显然需要一个绝对path -
我会对这种方法与数据库/ JCR存储库的缺点进行阐述感兴趣
不幸的是,@BalusC的FileServlet专注于下载文件,而他在上传文件方面的答案跳过了保存文件的部分。
一个易于转换为使用DB或JCR实现(如jackrabbit )的解决scheme将是更可取的。
将其存储在除了 IDE的项目文件夹(即服务器的部署文件夹) 之外的可访问位置的任何位置,原因仅在刷新页面后 ,“上载的映像”的答案中提到:
IDE的项目文件夹中的更改不会立即反映在服务器的工作文件夹中。 在IDE中有一种后台作业,它注意到服务器的工作文件夹与上次更新(在IDE中称为“发布”)是同步的。 这是你所看到的问题的主要原因。
在真实世界的代码中,存在将上传的文件存储在webapp的deploy文件夹中根本不起作用的情况。 一些服务器(默认或通过configuration)不会将已部署的WAR文件扩展到本地磁盘文件系统,而是完全在内存中。 如果没有基本编辑已部署的WAR文件并重新部署,则无法在内存中创build新文件。
即使服务器将已部署的WAR文件扩展到本地磁盘文件系统,所有新创build的文件在重新部署或甚至简单重新启动时都将丢失,只是因为这些新文件不是原始WAR文件的一部分。
只要你没有使用过getRealPath()
方法 ,那么对于我或者其他任何在本地磁盘文件系统中它将被保存的人来说都没有 getRealPath()
。 使用这种方法无论如何都是令人担忧的。
存储位置的path可以通过多种方式进行定义。 你必须自己做。 也许这是你的困惑造成的地方,因为你以某种方式期望服务器全部自动完成。 请注意, @MultipartConfig(location)
不指定最终的上传目标,但案例文件大小的临时存储位置超过了内存存储阈值。
所以,最终存储位置的path可以通过以下任一方式来定义:
-
硬编码:
File uploads = new File("/path/to/uploads");
-
通过
SET UPLOAD_LOCATION=/path/to/uploads
环境variablesSET UPLOAD_LOCATION=/path/to/uploads
:File uploads = new File(System.getenv("UPLOAD_LOCATION"));
-
通过
-Dupload.location="/path/to/uploads"
启动服务器时的VM参数:File uploads = new File(System.getProperty("upload.location"));
-
*.properties
文件条目为upload.location=/path/to/uploads
:File uploads = new File(properties.getProperty("upload.location"));
-
web.xml
<context-param>
带名称upload.location
和值/path/to/uploads
:File uploads = new File(getServletContext().getInitParameter("upload.location"));
-
如果有的话,使用服务器提供的位置,例如在JBoss AS / WildFly中 :
File uploads = new File(System.getProperty("jboss.server.data.dir"), "uploads");
无论哪种方式,您可以轻松地引用和保存该文件,如下所示:
File file = new File(uploads, "somefilename.ext"); try (InputStream input = part.getInputStream()) { Files.copy(input, file.toPath()); }
或者,当您要自动生成唯一的文件名以防止用户重写同名的现有文件时:
File file = File.createTempFile("somefilename-", ".ext", uploads); try (InputStream input = part.getInputStream()) { Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING); }
如何使用JSP / Servlet将file upload到服务器? 如何使用JSF 2.2 <h:inputFile>来上传文件? 保存的文件在哪里?
注意:不要使用Part#write()
来解释相对于@MultipartConfig(location)
定义的临时存储位置的path。
也可以看看:
- 如何将上传的文件保存在JSF中 (JSF定位,但原理几乎相同)
- 在Java Web应用程序中从应用程序服务器外部提供静态数据的最简单方法 (如果您想要将其提供回来)
- 如何在基于servlet的Web应用程序中暂时保存生成的文件
我根据公认的答案张贴我最后的做法:
@SuppressWarnings("serial") @WebServlet("/") @MultipartConfig public final class DataCollectionServlet extends Controller { private static final String UPLOAD_LOCATION_PROPERTY_KEY="upload.location"; private String uploadsDirName; @Override public void init() throws ServletException { super.init(); uploadsDirName = property(UPLOAD_LOCATION_PROPERTY_KEY); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Collection<Part> parts = req.getParts(); for (Part part : parts) { File save = new File(uploadsDirName, getFilename(part) + "_" + System.currentTimeMillis()); final String absolutePath = save.getAbsolutePath(); log.debug(absolutePath); part.write(absolutePath); sc.getRequestDispatcher(DATA_COLLECTION_JSP).forward(req, resp); } } // helpers private static String getFilename(Part part) { // courtesy of BalusC : http://stackoverflow.com/a/2424824/281545 for (String cd : part.getHeader("content-disposition").split(";")) { if (cd.trim().startsWith("filename")) { String filename = cd.substring(cd.indexOf('=') + 1).trim() .replace("\"", ""); return filename.substring(filename.lastIndexOf('/') + 1) .substring(filename.lastIndexOf('\\') + 1); // MSIE fix. } } return null; } }
其中:
@SuppressWarnings("serial") class Controller extends HttpServlet { static final String DATA_COLLECTION_JSP="/WEB-INF/jsp/data_collection.jsp"; static ServletContext sc; Logger log; // private // "/WEB-INF/app.properties" also works... private static final String PROPERTIES_PATH = "WEB-INF/app.properties"; private Properties properties; @Override public void init() throws ServletException { super.init(); // synchronize ! if (sc == null) sc = getServletContext(); log = LoggerFactory.getLogger(this.getClass()); try { loadProperties(); } catch (IOException e) { throw new RuntimeException("Can't load properties file", e); } } private void loadProperties() throws IOException { try(InputStream is= sc.getResourceAsStream(PROPERTIES_PATH)) { if (is == null) throw new RuntimeException("Can't locate properties file"); properties = new Properties(); properties.load(is); } } String property(final String key) { return properties.getProperty(key); } }
和/WEB-INF/app.properties:
upload.location=C:/_/
HTH,如果你发现一个错误让我知道