~~ Licensed to the Apache Software Foundation (ASF) under one ~~ or more contributor license agreements. See the NOTICE file ~~ distributed with this work for additional information ~~ regarding copyright ownership. The ASF licenses this file ~~ to you under the Apache License, Version 2.0 (the ~~ "License"); you may not use this file except in compliance ~~ with the License. You may obtain a copy of the License at ~~ ~~ http://www.apache.org/licenses/LICENSE-2.0 ~~ ~~ Unless required by applicable law or agreed to in writing, ~~ software distributed under the License is distributed on an ~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ~~ KIND, either express or implied. See the License for the ~~ specific language governing permissions and limitations ~~ under the License. -------------------------- Using Commons Fileupload 2 -------------------------- Using Commons FileUpload 2 FileUpload 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 {{{customizing.html}elsewhere}}. * How it works A file upload request comprises an ordered list of <> that are encoded according to {{{http://www.ietf.org/rfc/rfc1867.txt}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 <> interface, regardless of its underlying implementation. 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.html}Streaming API}}. Each file item has a number of properties that might be of interest for your application. For example, every item has a field name and a content type, and can provide an <> to access its data. On the other hand, you may need to process items differently, depending upon whether the item is a regular form field - that is, the data came from an ordinary text box or similar HTML field - or an uploaded file. The <> interface provides the methods to make such a determination, and to access the data in the most appropriate manner. FileUpload creates new file items using a <>. This is what gives FileUpload most of its flexibility. The store has ultimate control over how each item is created and stored. The store implementation that currently ships with FileUpload stores the item's data in memory or on disk, depending on the size of the item (i.e. bytes of data). However, this behavior can be customized to suit your application. * Servlets and Portlets Since 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: * Where you see references to the <> class, substitute the <> class. * Where you see references to the <> class, substitute the <> class. * Parsing the request Before 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 = HttpServletUploadRequest.isMultipartContent(request);]] --------------------------------------------------------------------------- Now we are ready to parse the request into its constituent items. * The simplest case The simplest usage scenario is the following: * Uploaded items should be retained in memory as long as they are reasonably small. * Larger items should be written to a temporary file on disk. * Very large upload requests should not be permitted. * The built-in defaults for the maximum size of an item to be retained in memory, the maximum permitted size of an upload request, and the location of temporary files are acceptable. [] Handling a request in this scenario couldn't be much simpler: --------------------------------------------------------------------------- // Create a store for disk-based file items DiskFileItemStore store = new DiskFileItemStore(); // Create a new file upload handler FileUpload upload = new FileUpload(); // Parse the request List items = store.parse(upload, request); --------------------------------------------------------------------------- That's all that's needed. Really! The result of the parse is a <> of file items, each of which implements the {{{apidocs/org/apache/commons/fileupload2/StoredFileItem.html}StoredFileItem}} interface. Processing these items is discussed below. * Exercising more control If 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 store or both. The following example shows several configuration options: --------------------------------------------------------------------------- // Create a store for disk-based file items DiskFileItemStore store = new DiskFileItemStore(); // Set store constraints store.setThreshold(yourMaxMemorySize); store.setRepository(yourTempDirectory); // Create a new file upload handler FileUpload upload = new FileUpload(); // Set overall request size constraint upload.setRequestSizeMax(yourMaxRequestSize); // Parse the request List items = store.parse(upload, request); --------------------------------------------------------------------------- Of course, each of the configuration methods is independent of the others. 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.html}customizing}} FileUpload. * Processing the uploaded items Once the parse has completed, you will have a <> of file items that you need to process. In most cases, you will want to handle file uploads differently from regular form fields, so you might process the list like this: --------------------------------------------------------------------------- // Process the uploaded items Iterator iter = items.iterator(); while (iter.hasNext()) { StoredFileItem item = (StoredFileItem) iter.next(); 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 <> value. The following example shows how to access these: --------------------------------------------------------------------------- // Process a regular form field if (item.isFormField()) { String name = item.getFieldName(); String value = Streams.asString(item); ... } --------------------------------------------------------------------------- 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.getFileName(); String contentType = item.getContentType(); boolean isInMemory = item.isInMemory(); long sizeInBytes = item.getContentLength(); ... --------------------------------------------------------------------------- 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) { File uploadedFile = new DiskFileItemStore().writeToFile(item); } else { InputStream uploadedStream = item.getInputStream(); ... uploadedStream.close(); } --------------------------------------------------------------------------- Note that, in the default implementation of FileUpload, <<>> will create a new file only, if that hasn't already be done. Actually copying the data is only done if the file item is still held in memory. If you do need to access the uploaded data in memory, you need simply call the <<>> method to obtain the data as an array of bytes. --------------------------------------------------------------------------- // Process a file upload in memory byte[] data = Streams.asByteArray(item); --------------------------------------------------------------------------- * Resource cleanup This section applies only, if you are using the {{{apidocs/org/apache/commons/fileupload2/disk/DiskFileItemStore.html}DiskFileItemStore}}. In other words, it applies, if your uploaded files are written to temporary files before processing them. Such temporary files can be deleted automatically, if they are no longer used (more precisely, if the corresponding instance of <<>> is garbage collected. This is done silently by the <<>> class, which starts a reaper thread. This reaper thread must be created and stopped, if it is no longer needed. In a servlet environment, this is done by using a special servlet context listener, called {{{apidocs/org/apache/commons/fileupload2/servlet/ServletFileTracker.html}ServletFileTracker}}. To do so, add a section like the following to your <<>>: --------------------------------------------------------------------------- ... org.apache.commons.fileupload2.servlet.ServletFileTracker ... --------------------------------------------------------------------------- Additionally, you must initialize your <<>> like this: --------------------------------------------------------------------------- ServletContext ctx = ...; /* Depends on your application, how to obtain * the ServletContext. In a servlet application, * there will be a possibility. */ DiskFileItemStore store = new DiskFileItemStore(); store.setTracker(ServletFileTracker.getFileTracker(ctx)); --------------------------------------------------------------------------- For a portlet environment, the same section in <<>> is required, but your code would look like this: --------------------------------------------------------------------------- PortletContext ctx = ...; /* Depends on your application, how to obtain * the PortletContext. In a portlet application, * there will be a possibility. */ DiskFileItemStore store = new DiskFileItemStore(); store.setTracker(PortletFileTracker.getFileTracker(ctx)); --------------------------------------------------------------------------- * Creating a DiskFileItemStore The <<>> provides an instance of <<>>. This instance must be used when creating a {{{apidocs/org/apache/commons/fileupload2/disk/DiskFileItemStore.html}DiskFileItemStore}}. This should be done by calling a method like the following: --------------------------------------------------------------------------- public static DiskFileItemStore newDiskFileItemStore(ServletContext pCtx, File pRepository) { FileCleaningTracker tracker = ServletFileTracker.getFileTracker(pCtx); DiskFileItemStore store = new DiskFileItemStore(); store.setRepository(pRepository); store.setTracker(tracker); return store; } --------------------------------------------------------------------------- * Disabling cleanup of temporary files To disable tracking of temporary files, you may set the <<>> to null. Consequently, created files will no longer be tracked. In particular, they will no longer be deleted automatically. * Interaction with virus scanners Virus scanners running on the same system as the web container can cause some unexpected behaviours for applications using FileUpload. This section describes some of the behaviours 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 progress If 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 an event listener: --------------------------------------------------------------------------- //Create an event listener EventListener listener = new DefaultEventListener(){ private final long APPROXIMATE_EVENT_INTERVAL = 100000; private int num; public long startItem(FileUpload pFileUpload, UploadRequest pRequest, FileItem pItem) throws IOException { ++num; System.out.println("Beginning to parse item " + num); return APPROXIMATE_EVENT_INTERVAL; } public long parsingItem(FileUpload pFileUpload, UploadRequest pRequest, FileItem pItem, long pCurrentLength) throws IOException { System.out.println("Still reading item " + num + ", " + pCurrentLength + " bytes read so far."); return APPROXIMATE_EVENT_INTERVAL; } public void endItem(FileUpload pFileUpload, UploadRequest pRequest, FileItem pItem, long pContentLength) throws IOException { System.out.println("Finished parsing item " + num + ", size = " + pContentLength + " bytes."); } }; // Install the event listener FileUpload upload = new FileUpload(); upload.setEventListener(listener); --------------------------------------------------------------------------- Do yourself a favour and implement your first event listener just like the above, but by changing the number APPROXIMATE_EVENT_INTERVAL to a small value, like 1. This will show you a pitfall: The event listener can be called quite frequently. Depending on the servlet engine and other environment factors, it may be called for any network packet, or even any single byte! In other words, your progress listener may become a performance problem! A typical solution might be, to reduce the event listeners activity by increasing the value of APPROXIMATE_EVENT_INTERVAL. * What's next Hopefully 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 {{{apidocs/index.html}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 {{{customizing.html}customization}} capabilities.