在 servlet 应用程序中保存上传文件的推荐方法 [英] Recommended way to save uploaded files in a servlet application

查看:33
本文介绍了在 servlet 应用程序中保存上传文件的推荐方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在此处读到,无论如何都不应该将文件保存在服务器中,因为它不可移植、不可交易且需要外部参数.但是,鉴于我需要一个用于 tomcat (7) 的 tmp 解决方案,并且我可以(相对)控制我想知道的服务器机器:

  • 保存文件的最佳位置是什么?我应该将它保存在 /WEB-INF/uploads(建议不要在这里)或下面的某个地方$CATALINA_BASE(请参阅此处)或...?JavaEE 6 教程 从用户获取路径(:wtf:).注意:该文件不应以任何方式下载.

  • 我应该在此处详细设置配置参数吗?我很欣赏一些代码(我宁愿给它一个相对路径 - 所以它至少是 Tomcat 可移植的) - Part.write() 看起来很有希望 - 但显然需要一个绝对路径

  • 我对说明这种方法与数据库/JCR 存储库的缺点很感兴趣

不幸的是,@BalusC 的 FileServlet 专注于下载文件,而他关于上传文件的回答跳过了保存文件的部分.

可轻松转换为使用 DB 或 JCR 实现的解决方案(例如 jackrabbit) 会更可取.

解决方案

将其存储在可访问位置的任何位置除了 IDE 的项目文件夹,也就是服务器的部署文件夹,原因在回答中提到上传的图片只有在刷新页面后才可用:

<块引用>

  1. IDE 项目文件夹中的更改不会立即反映在服务器的工作文件夹中.IDE 中有一种后台作业,它负责使服务器的工作文件夹与上次更新同步(这在 IDE 术语中称为发布").这是您所看到问题的主要原因.

  2. 在现实世界的代码中,在某些情况下,将上传的文件存储在 web 应用程序的部署文件夹中根本不起作用.一些服务器(默认或通过配置)不会将部署的 WAR 文件扩展到本地磁盘文件系统,而是完全在内存中.如果不对已部署的 WAR 文件进行基本编辑并重新部署,就无法在内存中创建新文件.

  3. 即使服务器将部署的 WAR 文件扩展到本地磁盘文件系统中,所有新创建的文件也会在重新部署甚至简单重启时丢失,因为这些新文件不是原始 WAR 的一部分文件.

对我或其他任何人来说,它会被保存在本地磁盘文件系统的确切位置真的无关紧要,只要您不要使用getRealPath()方法.使用这种方法在任何情况下都令人担忧.

存储位置的路径又可以通过多种方式定义.您必须自己完成这一切.也许这就是造成您困惑的地方,因为您以某种方式期望服务器自动执行所有操作.请注意,@MultipartConfig(location)指定最终上传目的地,但案例文件大小的临时存储位置超过了内存存储阈值.

因此,可以通过以下任一方式定义到最终存储位置的路径:

  • 硬编码:

     File uploads = new File("/path/to/uploads");

  • 环境变量通过SET 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 带有名称 upload.location 和值 /path/to/上传:

     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");试试 (InputStream input = part.getInputStream()) {Files.copy(input, file.toPath());}

或者,当您想要自动生成唯一的文件名以防止用户覆盖巧合名称相同的现有文件时:

File file = File.createTempFile("somefilename-", ".ext", uploads);试试 (InputStream input = part.getInputStream()) {Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING);}

如何在 JSP/Servlet 中获取 part如何使用 JSP/Servlet 将文件上传到服务器? 以及如何在 JSF 中获取 part如何使用 JSF 2.2 上传文件?保存的文件在哪里?

注意:不要使用Part#write() 因为它解释了相对于 @MultipartConfig(location)<中定义的临时存储位置的路径/代码>.还要确保您不会在读取/写入期间错误地使用 Reader/Writer 将字节转换为字符,从而损坏二进制文件,例如 PDF 文件或图像文件InputStream/OutputStream.

另见:

I read here that one should not save the file in the server anyway as it is not portable, transactional and requires external parameters. However, given that I need a tmp solution for tomcat (7) and that I have (relative) control over the server machine I want to know :

  • What is the best place to save the file ? Should I save it in /WEB-INF/uploads (advised against here) or someplace under $CATALINA_BASE (see here) or ... ? The JavaEE 6 tutorial gets the path from the user (:wtf:). NB : The file should not be downloadable by any means.

  • Should I set up a config parameter as detailed here ? I'd appreciate some code (I'd rather give it a relative path - so it is at least Tomcat portable) - Part.write() looks promising - but apparently needs a absolute path

  • I'd be interested in an exposition of the disadvantages of this approach vs a database/JCR repository one

Unfortunately the FileServlet by @BalusC concentrates on downloading files, while his answer on uploading files skips the part on where to save the file.

A solution easily convertible to use a DB or a JCR implementation (like jackrabbit) would be preferable.

解决方案

Store it anywhere in an accessible location except of the IDE's project folder aka the server's deploy folder, for reasons mentioned in the answer to Uploaded image only available after refreshing the page:

  1. Changes in the IDE's project folder does not immediately get reflected in the server's work folder. There's kind of a background job in the IDE which takes care that the server's work folder get synced with last updates (this is in IDE terms called "publishing"). This is the main cause of the problem you're seeing.

  2. In real world code there are circumstances where storing uploaded files in the webapp's deploy folder will not work at all. Some servers do (either by default or by configuration) not expand the deployed WAR file into the local disk file system, but instead fully in the memory. You can't create new files in the memory without basically editing the deployed WAR file and redeploying it.

  3. Even when the server expands the deployed WAR file into the local disk file system, all newly created files will get lost on a redeploy or even a simple restart, simply because those new files are not part of the original WAR file.

It really doesn't matter to me or anyone else where exactly on the local disk file system it will be saved, as long as you do not ever use getRealPath() method. Using that method is in any case alarming.

The path to the storage location can in turn be definied in many ways. You have to do it all by yourself. Perhaps this is where your confusion is caused because you somehow expected that the server does that all automagically. Please note that @MultipartConfig(location) does not specify the final upload destination, but the temporary storage location for the case file size exceeds memory storage threshold.

So, the path to the final storage location can be definied in either of the following ways:

  • Hardcoded:

      File uploads = new File("/path/to/uploads");
    

  • Environment variable via SET UPLOAD_LOCATION=/path/to/uploads:

      File uploads = new File(System.getenv("UPLOAD_LOCATION"));
    

  • VM argument during server startup via -Dupload.location="/path/to/uploads":

      File uploads = new File(System.getProperty("upload.location"));
    

  • *.properties file entry as upload.location=/path/to/uploads:

      File uploads = new File(properties.getProperty("upload.location"));
    

  • web.xml <context-param> with name upload.location and value /path/to/uploads:

      File uploads = new File(getServletContext().getInitParameter("upload.location"));
    

  • If any, use the server-provided location, e.g. in JBoss AS/WildFly:

      File uploads = new File(System.getProperty("jboss.server.data.dir"), "uploads");
    

Either way, you can easily reference and save the file as follows:

File file = new File(uploads, "somefilename.ext");

try (InputStream input = part.getInputStream()) {
    Files.copy(input, file.toPath());
}

Or, when you want to autogenerate an unique file name to prevent users from overwriting existing files with coincidentally the same name:

File file = File.createTempFile("somefilename-", ".ext", uploads);

try (InputStream input = part.getInputStream()) {
    Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}

How to obtain part in JSP/Servlet is answered in How to upload files to server using JSP/Servlet? and how to obtain part in JSF is answered in How to upload file using JSF 2.2 <h:inputFile>? Where is the saved File?

Note: do not use Part#write() as it interprets the path relative to the temporary storage location defined in @MultipartConfig(location). Also make absolutely sure that you aren't corrupting binary files such as PDF files or image files by converting bytes to characters during reading/writing by incorrectly using a Reader/Writer instead of InputStream/OutputStream.

See also:

这篇关于在 servlet 应用程序中保存上传文件的推荐方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆