#

Jsp Fileuploads

Apaches Fileupload API ist ein sehr nützliches Tool, um Dateiuploads aus Multipart-MIME Formularen einer Webseite in Jsps zu nutzen, da leider die HttpServletRequest Klassen solche Streams nicht automatisch Behandeln können.
Die Fileupload API enthält Klassen, welche die Datei Uploads in einem temporären Verzeichnis des Webservers ablegen (DefaultFileItem, DefaultFileItemFactory). Doch meistens möchte man diese Daten direkt verarbeiten oder in einer Datenbank speichern und der Umweg über das temporäre Uploadverzeichnis erscheint überflüssig, oder bereitet sogar wegen eines Virenscanners Probleme.
In solchen Fällen helfen die folgenden Erweiterungen der Fileupload API, welche die Fileuploads im Speicher halten.

Hierzu stellt die API das Interface FileItem zur Verfügung, welche die Behandlung einer einzelnen Datei aus dem Multipart-MIME Stream des Formulars zur Verfügung stellt:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
 
import org.apache.commons.fileupload.FileItem;
 
/**
 * Handles FileItem data in memory, without storing it to
 * a file cache.
 * Should not be used on very large datasets or uploads.
 *
 */
public class InMemoryFileItem implements FileItem {
/**
 * 
 */
private static final long serialVersionUID = 1L;
 
private byte[] data;
 
private String contentType;
 
private String name;
 
private boolean formField;
 
private String fieldName;
 
/**
 * @see org.apache.commons.fileupload.FileItem#getInputStream()
 */
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#getContentType()
 */
public String getContentType() {
return contentType;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#getName()
 */
public String getName() {
return name;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#isInMemory()
 */
public boolean isInMemory() {
return true;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#getSize()
 */
public long getSize() {
return data.length;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#get()
 */
public byte[] get() {
return data;
}
 
/**
 * Sets the data.
 * 
 * @param data
 *            the data
 */
public void set(byte[] data) {
this.data = data;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#getString(java.lang.String)
 */
public String getString(String encoding)
throws UnsupportedEncodingException {
return new String(data, encoding);
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#getString()
 */
public String getString() {
return new String(data);
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#write(java.io.File)
 */
public void write(File f) throws Exception {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
fos.write(data);
}
finally {
try {
fos.close();
} 
catch (Throwable t) {
// ignore
}
}
}
 
/**
 * Not used here.
 * 
 * @see org.apache.commons.fileupload.FileItem#delete()
 */
public void delete() {
//
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#getFieldName()
 */
public String getFieldName() {
return fieldName;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#setFieldName(java.lang.String)
 */
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#isFormField()
 */
public boolean isFormField() {
return formField;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#setFormField(boolean)
 */
public void setFormField(boolean formField) {
this.formField = formField;
}
 
/**
 * @see org.apache.commons.fileupload.FileItem#getOutputStream()
 */
public OutputStream getOutputStream() throws IOException {
return new OutputStream() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
 
public void write(int b) throws IOException {
out.write(b);
}
 
public void close() throws IOException {
out.close();
set(out.toByteArray());
}
 
public void flush() throws IOException {
out.flush();
}
 
public void write(byte[] b) throws IOException {
out.write(b);
}
 
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
}
};
}
}

Anschließend gilt es eine FileItemFactory für diese Fileitems zu erstellen:

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
 
/**
 * Factory for InMemoryFileItems.
 *
 */
public class InMemoryFileItemFactory implements FileItemFactory {
/**
 * @see org.apache.commons.fileupload.FileItemFactory#createItem(java.lang.String,
 *      java.lang.String, boolean, java.lang.String)
 */
public FileItem createItem(String fieldName, String contentType,
boolean isFormField, String fileName) {
InMemoryFileItem fi = new InMemoryFileItem();
fi.setFieldName(fieldName);
fi.setFormField(isFormField);
return fi;
}
}

Zu guter letzt gibt es natürlich eine Utility-Klasse, welche einfach einen HttpServletRequest entgegen nimmt und die ganzen Formulardaten in eine Map packt. Dabei haben die normalen Formularfelder den Typ String und die Dateiuploads sind byte-Arrays:

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
 
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
 
public class ServletRequestUtil {
private ServletRequestUtil(){}
 
public static Map readParameters(HttpServletRequest request, 
FileItemFactory factory) throws FileUploadException{
Map multiparts = new HashMap(64);
if (ServletFileUpload.isMultipartContent(request)){
// System.out.println("multipart");
ServletFileUpload sfu = new ServletFileUpload(factory);
sfu.setSizeMax(-1);
List fileItems = sfu.parseRequest(request);
Iterator iter = fileItems.iterator();
FileItem item;
String fieldname;
while (iter.hasNext()) {
item = (FileItem) iter.next();
fieldname = item.getFieldName();
if (item.isFormField()) multiparts.put(fieldname, item.getString());
else multiparts.put(fieldname, item.get());
}
}
else{
// System.out.println("NON multipart");
Enumeration parNames = request.getParameterNames();
String parName;
while (parNames.hasMoreElements()){
parName = (String) parNames.nextElement();
multiparts.put(parName, request.getParameter(parName));
}
}
// System.out.println(multiparts);
return multiparts;
}
 
public static Map readParameters(HttpServletRequest request)
throws FileUploadException {
return readParameters(request, new InMemoryFileItemFactory());
}
}

Der if-else Block in public static Map readParameters(HttpServletRequest request, FileItemFactory factory) unterscheidet automatisch zwischen Multipart-MIME Posts und normalen Posts, denn normale Posts werden von der HttpServletRequest-Klasse bereits korrekt verarbeitet und können nicht mit der Fileupload-API behandelt werden.

Die Utility-Klasse setzt natürlich voraus, dass man im Voraus weiss, unter welchem Schlüssel sich ein normales und unter welchem ein Dateiuopload Feld verbirgt.

Auch sollte man sich Gedanken darüber machen, wie Groß und wie viele Daten man im Hauptspeicher halten möchte. In dem Beispiel oben ist keine Grenze definiert, kann aber über sfu.setSizeMax(-1); mit einem Wert > 0 definiert werden.

Tags:, , ,

One Response to “Jsp Fileuploads” »»

  1. Comment by Secco | 19:46 18.01.09|X

    Dieser Artikel wurde im Newsletter von JspTutorial.org erwähnt.

Leave a Reply »»

Note: All comments are manually approved to avoid spam. So if your comment doesn't appear immediately, that's ok. Have patience, it can take some days until I have the time to approve my comments.