Using FileUploadFileUpload can be used in a number of different ways, depending upon the requirements of your application. In the simplest case, you will call a single method to parse the servlet request, and then process the list of items as they apply to your application. At the other end of the scale, you might decide to customize FileUpload to take full control of the way in which individual items are stored; for example, you might decide to stream the content into a database. Here, we will describe the basic principles of FileUpload, and illustrate some of the simpler - and most common - usage patterns. Customization of FileUpload is described elsewhere. FileUpload depends on Commons IO, so make sure you have the version mentioned on the dependencies page in your classpath before continuing. How it works
A file upload request comprises an ordered list of items that
are encoded according to
RFC 1867,
"Form-based File Upload in HTML". FileUpload can parse such a request
and provide your application with a list of the individual uploaded
items. Each such item implements the This page describes the traditional API of the commons fileupload library. The traditional API is a convenient approach. However, for ultimate performance, you might prefer the faster Streaming API.
Each file item has a number of properties that might be of interest for
your application. For example, every item has a name and a content type,
and can provide an
FileUpload creates new file items using a Servlets, Jakarta Servlets, and PortletsStarting with version 1.1, FileUpload supports file upload requests in both servlet and portlet environments. The usage is almost identical in the two environments, so the remainder of this document refers only to the servlet environment. If you are building a portlet application, the following are the two distinctions you should make as you read this document:
Version 2 of FileUpload introduces support for the Jakarta Servlet API 5.
(This API is the successor to the classic servlet environment, which
basically renames the
Parsing the requestBefore you can work with the uploaded items, of course, you need to parse the request itself. Ensuring that the request is actually a file upload request is straightforward, but FileUpload makes it simplicity itself, by providing a static method to do just that. // Check that we have a file upload request boolean isMultipart = ServletFileUpload.isMultipartContent(request); Now we are ready to parse the request into its constituent items. The simplest caseThe simplest usage scenario is the following:
Handling a request in this scenario couldn't be much simpler: // Create a factory for disk-based file items DiskFileItemFactory factory = new DiskFileItemFactory(); // Configure a repository (to ensure a secure temp location is used) ServletContext servletContext = this.getServletConfig().getServletContext(); File repository = (File) servletContext.getAttribute("jakarta.servlet.context.tempdir"); // Or "javax.servlet.context.tempdir" for javax factory.setRepository(repository); // Create a new file upload handler JakartaServletDiskFileUpload upload = new JakartaServletDiskFileUpload(factory); // Parse the request List<DiskFileItem> items = upload.parseRequest(request); That's all that's needed. Really!
The result of the parse is a Exercising more controlIf your usage scenario is close to the simplest case, described above, but you need a little more control, you can easily customize the behavior of the upload handler or the file item factory or both. The following example shows several configuration options: // Create a factory for disk-based file items DiskFileItemFactory factory = new DiskFileItemFactory() // Set factory constraints .setSizeThreshold(yourMaxMemorySize) .setPath(yourTempDirectoryPath) .get(); // Create a new file upload handler JakartaServletDiskFileUpload upload = new JakartaServletDiskFileUpload(factory); // Set overall request size constraint upload.setFileSizeMax(yourMaxRequestSize); // Parse the request List<DiskFileItem> items = upload.parseRequest(request); Of course, each of the configuration methods is independent of the others, but if you want to configure the factory all at once, you can do that with an alternative constructor, like this: // Create a factory for disk-based file items DiskFileItemFactory factory = new DiskFileItemFactory() // Set factory constraints .setSizeThreshold(yourMaxMemorySize) .setPath(yourTempDirectoryPath) .get(); Should you need further control over the parsing of the request, such as storing the items elsewhere - for example, in a database - you will need to look into customizing FileUpload. Processing the uploaded items
Once the parse has completed, you will have a // Process the uploaded items for (FileItem item : items.iterator()) { if (item.isFormField()) { processFormField(item); } else { processUploadedFile(item); } }
For a regular form field, you will most likely be interested only in the
name of the item, and its // Process a regular form field if (item.isFormField()) { String name = item.getFieldName(); String value = item.getString(); ... } For a file upload, there are several different things you might want to know before you process the content. Here is an example of some of the methods you might be interested in. // Process a file upload if (!item.isFormField()) { String fieldName = item.getFieldName(); String fileName = item.getName(); String contentType = item.getContentType(); boolean isInMemory = item.isInMemory(); long sizeInBytes = item.getSize(); ... } With uploaded files, you generally will not want to access them via memory, unless they are small, or unless you have no other alternative. Rather, you will want to process the content as a stream, or write the entire file to its ultimate location. FileUpload provides simple means of accomplishing both of these. // Process a file upload if (writeToFile) { Path uploadedFile = Paths.get(...); item.write(uploadedFile); } else { InputStream uploadedStream = item.getInputStream(); ... uploadedStream.close(); }
Note that, in the default implementation of FileUpload,
If you do need to access the uploaded data in memory, you need simply
call the // Process a file upload in memory byte[] data = item.get(); ... Resource cleanupThis section applies only, if you are using the DiskFileItem. In other words, it applies, if your uploaded files are written to temporary files before processing them.
Such temporary files are deleted automatically, if they are no longer
used (more precisely, if the corresponding instance of
This reaper thread should be stopped, if it is no longer needed. In
a servlet environment, this is done by using a special servlet
context listener, called
JakartaFileCleaner.
To do so, add a section like the following to your <web-app> ... <listener> <listener-class> org.apache.commons.fileupload2.jakarta.JakartaFileCleaner </listener-class> </listener> ... </web-app> Creating a DiskFileItemFactory
The JakartaFileCleaner provides an instance of
public static DiskFileItemFactory newDiskFileItemFactory(ServletContext context, File repository) { FileCleaningTracker fileCleaningTracker = JakartaFileCleaner.getFileCleaningTracker(context); DiskFileItemFactory factory = new DiskFileItemFactory() .setSizeThreshold(DiskFileItemFactory.DEFAULT_THRESHOLD) .setPath(repository) .get(); factory.setFileCleaningTracker(fileCleaningTracker); return factory; } Disabling cleanup of temporary files
To disable tracking of temporary files, you may set the
Interaction with virus scannersVirus scanners running on the same system as the web container can cause some unexpected behaviors for applications using FileUpload. This section describes some of the behaviors that you might encounter, and provides some ideas for how to handle them. The default implementation of FileUpload will cause uploaded items above a certain size threshold to be written to disk. As soon as such a file is closed, any virus scanner on the system will wake up and inspect it, and potentially quarantine the file - that is, move it to a special location where it will not cause problems. This, of course, will be a surprise to the application developer, since the uploaded file item will no longer be available for processing. On the other hand, uploaded items below that same threshold will be held in memory, and therefore will not be seen by virus scanners. This allows for the possibility of a virus being retained in some form (although if it is ever written to disk, the virus scanner would locate and inspect it). One commonly used solution is to set aside one directory on the system into which all uploaded files will be placed, and to configure the virus scanner to ignore that directory. This ensures that files will not be ripped out from under the application, but then leaves responsibility for virus scanning up to the application developer. Scanning the uploaded files for viruses can then be performed by an external process, which might move clean or cleaned files to an "approved" location, or by integrating a virus scanner within the application itself. The details of configuring an external process or integrating virus scanning into an application are outside the scope of this document. Watching progressIf you expect really large file uploads, then it would be nice to report to your users, how much is already received. Even HTML pages allow to implement a progress bar by returning a multipart/replace response, or something like that. Watching the upload progress may be done by supplying a progress listener: //Create a progress listener ProgressListener progressListener = new ProgressListener(){ public void update(long bytesRead, long contentLength, int items) { System.out.println("We are currently reading item " + items); if (contentLength == -1) { System.out.println("So far, " + bytesRead + " bytes have been read."); } else { System.out.println("So far, " + bytesRead + " of " + contentLength + " bytes have been read."); } } }; upload.setProgressListener(progressListener); Do yourself a favour and implement your first progress listener just like the above, because it shows you a pitfall: The progress listener is called quite frequently. Depending on the servlet engine and other environment factory, it may be called for any network packet! In other words, your progress listener may become a performance problem! A typical solution might be, to reduce the progress listeners activity. For example, you might emit a message only, if the number of megabytes has changed: //Create a progress listener ProgressListener progressListener = new ProgressListener(){ private long megaBytes = -1; public void update(long bytesRead, long contentLength, int items) { long mBytes = bytesRead / 1000000; if (megaBytes == mBytes) { return; } megaBytes = mBytes; System.out.println("We are currently reading item " + items); if (contentLength == -1) { System.out.println("So far, " + bytesRead + " bytes have been read."); } else { System.out.println("So far, " + bytesRead + " of " + contentLength + " bytes have been read."); } } }; What's nextHopefully this page has provided you with a good idea of how to use FileUpload in your own applications. For more detail on the methods introduced here, as well as other available methods, you should refer to the Javadocs. The usage described here should satisfy a large majority of file upload needs. However, should you have more complex requirements, FileUpload should still be able to help you, with it's flexible customization capabilities. |