Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

tomcat - Recommended way to save uploaded files in a servlet application

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.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
...