availability: January 2013
Having examined the rails alternatives for large file upload, we turned towards other alternatives and YES , did we find one!
It's the java fileupload component from the apache commons project. It has the following features:
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// Set factory constraints
factory.setSizeThreshold(yourMaxMemorySize);
factory.setRepository(yourTempDirectory);
// Create a new file upload handler ServletFileUpload upload = new ServletFileUpload(factory);
// Set overall request size constraint upload.setSizeMax(yourMaxRequestSize);
// Parse the request List /* FileItem */ items = upload.parseRequest(request);
// Process the uploaded items
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (item.isFormField()) {
processFormField(item);
} else {
processUploadedFile(item);
}
}
You access the file characteristics like this:
// 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();
...
}
default implementation of FileUpload, write() will attempt to rename the file to the specified destination, but if you want you can read the stream directly.
// Process a file upload
if (writeToFile) {
File uploadedFile = new File(...);
item.write(uploadedFile);
} else {
InputStream uploadedStream = item.getInputStream();
...
uploadedStream.close();
}
The streaming API:
// 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. Here's how we do it:
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();
// Parse the request
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (item.isFormField()) {
System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected.");
} else {
System.out.println("File field " + name + " with file name " + item.getName() + " detected.");
// Process the input stream
...
}
}
How to write is to disk most efficiently:
FileOutputStream fout= new FileOutputStream (yourPathtowriteto);
int byte_;
while ((byte_=stream.read()) != -1)
{
fout.write(byte_);
}
fout.close();
Option 2: We use bufferstreams to do the job
FileOutputStream fout= new FileOutputStream ( yourPathtowriteto ); BufferedOutputStream bout= new BufferedOutputStream (fout); BufferedInputStream bin= new BufferedInputStream(stream);Option 3: Use a byte array instead of per byte. You can experiment with different buffersize to see the effect. This depends on your filesystem blocksize and your disk cache size and so on.
int byte_; while ((byte_=bin.read()) != -1) { bout.write(byte_); } bout.close(); bin.close();
FileOutputStream fout= new FileOutputStream (yourPathtowriteto);
BufferedOutputStream bout= new BufferedOutputStream (fout);
BufferedInputStream bin= new BufferedInputStream(stream);
byte buf[] = new byte[2048];
while ((bin.read(buf)) != -1)
{
bout.write(buf);
}
bout.close();
bin.close();
Option 4: Using a *static* byte array: to avoid reallocation , we create a final buffer and use synchronized to control access to it.
static final int BUFF_SIZE = 100000; static final byte[] buffer = new byte[BUFF_SIZE];Controlling network I/O in your appserver
FileOutputStream fout= new FileOutputStream (yourPathtowriteto); while (true) { synchronized (buffer) { int amountRead = stream.read(buffer); if (amountRead == -1) { break; } fout.write(buffer, 0, amountRead); } }