diff --git a/build.third_step1.gradle b/build.third_step1.gradle index 71d9d72ce..ad905a76c 100644 --- a/build.third_step1.gradle +++ b/build.third_step1.gradle @@ -17,7 +17,7 @@ def srcDir="." task unJar{ - ant{ + ant{ delete(dir:"build") } } @@ -31,6 +31,7 @@ sourceSets{ "${srcDir}/fine-poi/src", "${srcDir}/fine-quartz/src", "${srcDir}/fine-commons-io/src", + "${srcDir}/fine-commons-fileupload/src", "${srcDir}/fine-commons-lang3/src", "${srcDir}/fine-commons-collections4/src" ] @@ -42,7 +43,7 @@ sourceSets{ sourceSets.main.output.classesDir = file('build/classes/1') repositories{ mavenCentral() -} +} //获取什么分支名 FileTree files =fileTree(dir:'./',include:'build.*.gradle') @@ -77,6 +78,7 @@ task copyFiles(type:Copy,dependsOn:'compileJava'){ with dataContent.call("${srcDir}/fine-poi/src") with dataContent.call("${srcDir}/fine-quartz/src") with dataContent.call("${srcDir}/fine-commons-io/src") + with dataContent.call("${srcDir}/fine-commons-fileupload/src") into "${classesDir}" } } diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DefaultFileItem.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DefaultFileItem.java new file mode 100755 index 000000000..a4ed62da8 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DefaultFileItem.java @@ -0,0 +1,80 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.File; +import com.fr.third.org.apache.commons.fileupload.disk.DiskFileItem; + + +/** + *

The default implementation of the + * {@link com.fr.third.org.apache.commons.fileupload.FileItem FileItem} interface. + * + *

After retrieving an instance of this class from a {@link + * com.fr.third.org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see + * {@link com.fr.third.org.apache.commons.fileupload.DiskFileUpload + * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may + * either request all contents of file at once using {@link #get()} or + * request an {@link java.io.InputStream InputStream} with + * {@link #getInputStream()} and process the file without attempting to load + * it into memory, which may come handy with large files. + * + * @author Rafal Krzewski + * @author Sean Legassick + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id$ + * + * @deprecated Use DiskFileItem instead. + */ +public class DefaultFileItem + extends DiskFileItem { + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs a new DefaultFileItem instance. + * + * @param fieldName The name of the form field. + * @param contentType The content type passed by the browser or + * null if not specified. + * @param isFormField Whether or not this item is a plain form field, as + * opposed to a file upload. + * @param fileName The original filename in the user's filesystem, or + * null if not specified. + * @param sizeThreshold The threshold, in bytes, below which items will be + * retained in memory and above which they will be + * stored as a file. + * @param repository The data repository, which is the directory in + * which files will be created, should the item size + * exceed the threshold. + * + * @deprecated Use DiskFileItem instead. + */ + public DefaultFileItem(String fieldName, String contentType, + boolean isFormField, String fileName, int sizeThreshold, + File repository) { + super(fieldName, contentType, isFormField, fileName, sizeThreshold, + repository); + } + + +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DefaultFileItemFactory.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DefaultFileItemFactory.java new file mode 100755 index 000000000..5ffdd0d52 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DefaultFileItemFactory.java @@ -0,0 +1,107 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.File; +import com.fr.third.org.apache.commons.fileupload.disk.DiskFileItemFactory; + +/** + *

The default {@link com.fr.third.org.apache.commons.fileupload.FileItemFactory} + * implementation. This implementation creates + * {@link com.fr.third.org.apache.commons.fileupload.FileItem} instances which keep their + * content either in memory, for smaller items, or in a temporary file on disk, + * for larger items. The size threshold, above which content will be stored on + * disk, is configurable, as is the directory in which temporary files will be + * created.

+ * + *

If not otherwise configured, the default configuration values are as + * follows: + *

+ *

+ * + * @author Martin Cooper + * + * @version $Id$ + * + * @deprecated Use DiskFileItemFactory instead. + */ +public class DefaultFileItemFactory extends DiskFileItemFactory { + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs an unconfigured instance of this class. The resulting factory + * may be configured by calling the appropriate setter methods. + * + * @deprecated Use DiskFileItemFactory instead. + */ + public DefaultFileItemFactory() { + super(); + } + + + /** + * Constructs a preconfigured instance of this class. + * + * @param sizeThreshold The threshold, in bytes, below which items will be + * retained in memory and above which they will be + * stored as a file. + * @param repository The data repository, which is the directory in + * which files will be created, should the item size + * exceed the threshold. + * + * @deprecated Use DiskFileItemFactory instead. + */ + public DefaultFileItemFactory(int sizeThreshold, File repository) { + super(sizeThreshold, repository); + } + + + // --------------------------------------------------------- Public Methods + + /** + * Create a new {@link com.fr.third.org.apache.commons.fileupload.DefaultFileItem} + * instance from the supplied parameters and the local factory + * configuration. + * + * @param fieldName The name of the form field. + * @param contentType The content type of the form field. + * @param isFormField true if this is a plain form field; + * false otherwise. + * @param fileName The name of the uploaded file, if any, as supplied + * by the browser or other client. + * + * @return The newly created file item. + * + * @deprecated Use DiskFileItemFactory instead. + */ + public FileItem createItem( + String fieldName, + String contentType, + boolean isFormField, + String fileName + ) { + return new DefaultFileItem(fieldName, contentType, + isFormField, fileName, getSizeThreshold(), getRepository()); + } + +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DiskFileUpload.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DiskFileUpload.java new file mode 100755 index 000000000..420a78273 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/DiskFileUpload.java @@ -0,0 +1,212 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.File; +import java.util.List; +import javax.servlet.http.HttpServletRequest; + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(HttpServletRequest)} to acquire a list of {@link + * com.fr.third.org.apache.commons.fileupload.FileItem}s associated with a given HTML + * widget.

+ * + *

Individual parts will be stored in temporary disk storage or in memory, + * depending on their size, and will be available as {@link + * com.fr.third.org.apache.commons.fileupload.FileItem}s.

+ * + * @author Rafal Krzewski + * @author Daniel Rall + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id$ + * + * @deprecated Use ServletFileUpload together with + * DiskFileItemFactory instead. + */ +public class DiskFileUpload + extends FileUploadBase { + + // ----------------------------------------------------------- Data members + + + /** + * The factory to use to create new form items. + */ + private DefaultFileItemFactory fileItemFactory; + + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs an instance of this class which uses the default factory to + * create FileItem instances. + * + * @see #DiskFileUpload(DefaultFileItemFactory fileItemFactory) + * + * @deprecated Use FileUpload instead. + */ + public DiskFileUpload() { + super(); + this.fileItemFactory = new DefaultFileItemFactory(); + } + + + /** + * Constructs an instance of this class which uses the supplied factory to + * create FileItem instances. + * + * @see #DiskFileUpload() + * @param fileItemFactory The file item factory to use. + * + * @deprecated Use FileUpload instead. + */ + public DiskFileUpload(DefaultFileItemFactory fileItemFactory) { + super(); + this.fileItemFactory = fileItemFactory; + } + + + // ----------------------------------------------------- Property accessors + + + /** + * Returns the factory class used when creating file items. + * + * @return The factory class for new file items. + * + * @deprecated Use FileUpload instead. + */ + public FileItemFactory getFileItemFactory() { + return fileItemFactory; + } + + + /** + * Sets the factory class to use when creating file items. The factory must + * be an instance of DefaultFileItemFactory or a subclass + * thereof, or else a ClassCastException will be thrown. + * + * @param factory The factory class for new file items. + * + * @deprecated Use FileUpload instead. + */ + public void setFileItemFactory(FileItemFactory factory) { + this.fileItemFactory = (DefaultFileItemFactory) factory; + } + + + /** + * Returns the size threshold beyond which files are written directly to + * disk. + * + * @return The size threshold, in bytes. + * + * @see #setSizeThreshold(int) + * + * @deprecated Use DiskFileItemFactory instead. + */ + public int getSizeThreshold() { + return fileItemFactory.getSizeThreshold(); + } + + + /** + * Sets the size threshold beyond which files are written directly to disk. + * + * @param sizeThreshold The size threshold, in bytes. + * + * @see #getSizeThreshold() + * + * @deprecated Use DiskFileItemFactory instead. + */ + public void setSizeThreshold(int sizeThreshold) { + fileItemFactory.setSizeThreshold(sizeThreshold); + } + + + /** + * Returns the location used to temporarily store files that are larger + * than the configured size threshold. + * + * @return The path to the temporary file location. + * + * @see #setRepositoryPath(String) + * + * @deprecated Use DiskFileItemFactory instead. + */ + public String getRepositoryPath() { + return fileItemFactory.getRepository().getPath(); + } + + + /** + * Sets the location used to temporarily store files that are larger + * than the configured size threshold. + * + * @param repositoryPath The path to the temporary file location. + * + * @see #getRepositoryPath() + * + * @deprecated Use DiskFileItemFactory instead. + */ + public void setRepositoryPath(String repositoryPath) { + fileItemFactory.setRepository(new File(repositoryPath)); + } + + + // --------------------------------------------------------- Public methods + + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. If files are stored + * on disk, the path is given by getRepository(). + * + * @param req The servlet request to be parsed. Must be non-null. + * @param sizeThreshold The max size in bytes to be stored in memory. + * @param sizeMax The maximum allowed upload size, in bytes. + * @param path The location where the files should be stored. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * + * @deprecated Use ServletFileUpload instead. + */ + public List /* FileItem */ parseRequest(HttpServletRequest req, + int sizeThreshold, + long sizeMax, String path) + throws FileUploadException { + setSizeThreshold(sizeThreshold); + setSizeMax(sizeMax); + setRepositoryPath(path); + return parseRequest(req); + } + +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItem.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItem.java new file mode 100755 index 000000000..905180a79 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItem.java @@ -0,0 +1,226 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; + +/** + *

This class represents a file or form item that was received within a + * multipart/form-data POST request. + * + *

After retrieving an instance of this class from a {@link + * com.fr.third.org.apache.commons.fileupload.FileUpload FileUpload} instance (see + * {@link com.fr.third.org.apache.commons.fileupload.FileUpload + * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may + * either request all contents of the file at once using {@link #get()} or + * request an {@link java.io.InputStream InputStream} with + * {@link #getInputStream()} and process the file without attempting to load + * it into memory, which may come handy with large files. + * + *

While this interface does not extend + * javax.activation.DataSource per se (to avoid a seldom used + * dependency), several of the defined methods are specifically defined with + * the same signatures as methods in that interface. This allows an + * implementation of this interface to also implement + * javax.activation.DataSource with minimal additional work. + * + * @author Rafal Krzewski + * @author Sean Legassick + * @author Jason van Zyl + * @author Martin Cooper + * + * @version $Id$ + */ +public interface FileItem + extends Serializable { + + + // ------------------------------- Methods from javax.activation.DataSource + + + /** + * Returns an {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @return An {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @throws IOException if an error occurs. + */ + InputStream getInputStream() + throws IOException; + + + /** + * Returns the content type passed by the browser or null if + * not defined. + * + * @return The content type passed by the browser or null if + * not defined. + */ + String getContentType(); + + + /** + * Returns the original filename in the client's filesystem, as provided by + * the browser (or other client software). In most cases, this will be the + * base file name, without path information. However, some clients, such as + * the Opera browser, do include path information. + * + * @return The original filename in the client's filesystem. + */ + String getName(); + + + // ------------------------------------------------------- FileItem methods + + + /** + * Provides a hint as to whether or not the file contents will be read + * from memory. + * + * @return true if the file contents will be read from memory; + * false otherwise. + */ + boolean isInMemory(); + + + /** + * Returns the size of the file item. + * + * @return The size of the file item, in bytes. + */ + long getSize(); + + + /** + * Returns the contents of the file item as an array of bytes. + * + * @return The contents of the file item as an array of bytes. + */ + byte[] get(); + + + /** + * Returns the contents of the file item as a String, using the specified + * encoding. This method uses {@link #get()} to retrieve the + * contents of the item. + * + * @param encoding The character encoding to use. + * + * @return The contents of the item, as a string. + * + * @throws UnsupportedEncodingException if the requested character + * encoding is not available. + */ + String getString(String encoding) + throws UnsupportedEncodingException; + + + /** + * Returns the contents of the file item as a String, using the default + * character encoding. This method uses {@link #get()} to retrieve the + * contents of the item. + * + * @return The contents of the item, as a string. + */ + String getString(); + + + /** + * A convenience method to write an uploaded item to disk. The client code + * is not concerned with whether or not the item is stored in memory, or on + * disk in a temporary location. They just want to write the uploaded item + * to a file. + *

+ * This method is not guaranteed to succeed if called more than once for + * the same item. This allows a particular implementation to use, for + * example, file renaming, where possible, rather than copying all of the + * underlying data, thus gaining a significant performance benefit. + * + * @param file The File into which the uploaded item should + * be stored. + * + * @throws Exception if an error occurs. + */ + void write(File file) throws Exception; + + + /** + * Deletes the underlying storage for a file item, including deleting any + * associated temporary disk file. Although this storage will be deleted + * automatically when the FileItem instance is garbage + * collected, this method can be used to ensure that this is done at an + * earlier time, thus preserving system resources. + */ + void delete(); + + + /** + * Returns the name of the field in the multipart form corresponding to + * this file item. + * + * @return The name of the form field. + */ + String getFieldName(); + + + /** + * Sets the field name used to reference this file item. + * + * @param name The name of the form field. + */ + void setFieldName(String name); + + + /** + * Determines whether or not a FileItem instance represents + * a simple form field. + * + * @return true if the instance represents a simple form + * field; false if it represents an uploaded file. + */ + boolean isFormField(); + + + /** + * Specifies whether or not a FileItem instance represents + * a simple form field. + * + * @param state true if the instance represents a simple form + * field; false if it represents an uploaded file. + */ + void setFormField(boolean state); + + + /** + * Returns an {@link java.io.OutputStream OutputStream} that can + * be used for storing the contents of the file. + * + * @return An {@link java.io.OutputStream OutputStream} that can be used + * for storing the contensts of the file. + * + * @throws IOException if an error occurs. + */ + OutputStream getOutputStream() throws IOException; + +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemFactory.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemFactory.java new file mode 100755 index 000000000..952189023 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemFactory.java @@ -0,0 +1,50 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + + +/** + *

A factory interface for creating {@link FileItem} instances. Factories + * can provide their own custom configuration, over and above that provided + * by the default file upload implementation.

+ * + * @author Martin Cooper + * + * @version $Id$ + */ +public interface FileItemFactory { + + /** + * Create a new {@link FileItem} instance from the supplied parameters and + * any local factory configuration. + * + * @param fieldName The name of the form field. + * @param contentType The content type of the form field. + * @param isFormField true if this is a plain form field; + * false otherwise. + * @param fileName The name of the uploaded file, if any, as supplied + * by the browser or other client. + * + * @return The newly created file item. + */ + FileItem createItem( + String fieldName, + String contentType, + boolean isFormField, + String fileName + ); +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemIterator.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemIterator.java new file mode 100755 index 000000000..607f12bdf --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemIterator.java @@ -0,0 +1,48 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; +import java.io.IOException; + + +/** + * An iterator, as returned by + * {@link FileUploadBase#getItemIterator(RequestContext)}. + */ +public interface FileItemIterator { + /** + * Returns, whether another instance of {@link FileItemStream} + * is available. + * @throws FileUploadException Parsing or processing the + * file item failed. + * @throws IOException Reading the file item failed. + * @return True, if one or more additional file items + * are available, otherwise false. + */ + boolean hasNext() throws FileUploadException, IOException; + + /** + * Returns the next available {@link FileItemStream}. + * @throws java.util.NoSuchElementException No more items are available. Use + * {@link #hasNext()} to prevent this exception. + * @throws FileUploadException Parsing or processing the + * file item failed. + * @throws IOException Reading the file item failed. + * @return FileItemStream instance, which provides + * access to the next file item. + */ + FileItemStream next() throws FileUploadException, IOException; +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemStream.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemStream.java new file mode 100755 index 000000000..a1f0813c4 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileItemStream.java @@ -0,0 +1,97 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.IOException; +import java.io.InputStream; + + +/** + *

This interface provides access to a file or form item that was + * received within a multipart/form-data POST request. + * The items contents are retrieved by calling {@link #openStream()}.

+ *

Instances of this class are created by accessing the + * iterator, returned by + * {@link FileUploadBase#getItemIterator(RequestContext)}.

+ *

Note: There is an interaction between the iterator and + * its associated instances of {@link FileItemStream}: By invoking + * {@link java.util.Iterator#hasNext()} on the iterator, you discard all data, + * which hasn't been read so far from the previous data.

+ */ +public interface FileItemStream { + /** + * This exception is thrown, if an attempt is made to read + * data from the {@link InputStream}, which has been returned + * by {@link FileItemStream#openStream()}, after + * {@link java.util.Iterator#hasNext()} has been invoked on the + * iterator, which created the {@link FileItemStream}. + */ + public static class ItemSkippedException extends IOException { + /** + * The exceptions serial version UID, which is being used + * when serializing an exception instance. + */ + private static final long serialVersionUID = -7280778431581963740L; + } + + /** Creates an {@link InputStream}, which allows to read the + * items contents. + * @return The input stream, from which the items data may + * be read. + * @throws IllegalStateException The method was already invoked on + * this item. It is not possible to recreate the data stream. + * @throws IOException An I/O error occurred. + * @see ItemSkippedException + */ + InputStream openStream() throws IOException; + + /** + * Returns the content type passed by the browser or null if + * not defined. + * + * @return The content type passed by the browser or null if + * not defined. + */ + String getContentType(); + + /** + * Returns the original filename in the client's filesystem, as provided by + * the browser (or other client software). In most cases, this will be the + * base file name, without path information. However, some clients, such as + * the Opera browser, do include path information. + * + * @return The original filename in the client's filesystem. + */ + String getName(); + + /** + * Returns the name of the field in the multipart form corresponding to + * this file item. + * + * @return The name of the form field. + */ + String getFieldName(); + + /** + * Determines whether or not a FileItem instance represents + * a simple form field. + * + * @return true if the instance represents a simple form + * field; false if it represents an uploaded file. + */ + boolean isFormField(); +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUpload.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUpload.java new file mode 100755 index 000000000..d5fb14cd9 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUpload.java @@ -0,0 +1,106 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(javax.servlet.http.HttpServletRequest)} to acquire a list + * of {@link com.fr.third.org.apache.commons.fileupload.FileItem FileItems} associated + * with a given HTML widget.

+ * + *

How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.

+ * + * @author Rafal Krzewski + * @author Daniel Rall + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id$ + */ +public class FileUpload + extends FileUploadBase { + + // ----------------------------------------------------------- Data members + + + /** + * The factory to use to create new form items. + */ + private FileItemFactory fileItemFactory; + + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs an uninitialised instance of this class. A factory must be + * configured, using setFileItemFactory(), before attempting + * to parse requests. + * + * @see #FileUpload(FileItemFactory) + */ + public FileUpload() { + super(); + } + + + /** + * Constructs an instance of this class which uses the supplied factory to + * create FileItem instances. + * + * @see #FileUpload() + * @param fileItemFactory The factory to use for creating file items. + */ + public FileUpload(FileItemFactory fileItemFactory) { + super(); + this.fileItemFactory = fileItemFactory; + } + + + // ----------------------------------------------------- Property accessors + + + /** + * Returns the factory class used when creating file items. + * + * @return The factory class for new file items. + */ + public FileItemFactory getFileItemFactory() { + return fileItemFactory; + } + + + /** + * Sets the factory class to use when creating file items. + * + * @param factory The factory class for new file items. + */ + public void setFileItemFactory(FileItemFactory factory) { + this.fileItemFactory = factory; + } + + +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUploadBase.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUploadBase.java new file mode 100755 index 000000000..ee9e8290e --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUploadBase.java @@ -0,0 +1,1186 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import javax.servlet.http.HttpServletRequest; + +import com.fr.third.org.apache.commons.fileupload.servlet.ServletFileUpload; +import com.fr.third.org.apache.commons.fileupload.servlet.ServletRequestContext; +import com.fr.third.org.apache.commons.fileupload.util.Closeable; +import com.fr.third.org.apache.commons.fileupload.util.LimitedInputStream; +import com.fr.third.org.apache.commons.fileupload.util.Streams; + + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(HttpServletRequest)} to acquire a list of {@link + * com.fr.third.org.apache.commons.fileupload.FileItem}s associated with a given HTML + * widget.

+ * + *

How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.

+ * + * @author Rafal Krzewski + * @author Daniel Rall + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id$ + */ +public abstract class FileUploadBase { + + // ---------------------------------------------------------- Class methods + + + /** + *

Utility method that determines whether the request contains multipart + * content.

+ * + *

NOTE:This method will be moved to the + * ServletFileUpload class after the FileUpload 1.1 release. + * Unfortunately, since this method is static, it is not possible to + * provide its replacement until this method is removed.

+ * + * @param ctx The request context to be evaluated. Must be non-null. + * + * @return true if the request is multipart; + * false otherwise. + */ + public static final boolean isMultipartContent(RequestContext ctx) { + String contentType = ctx.getContentType(); + if (contentType == null) { + return false; + } + if (contentType.toLowerCase().startsWith(MULTIPART)) { + return true; + } + return false; + } + + + /** + * Utility method that determines whether the request contains multipart + * content. + * + * @param req The servlet request to be evaluated. Must be non-null. + * + * @return true if the request is multipart; + * false otherwise. + * + * @deprecated Use the method on ServletFileUpload instead. + */ + public static boolean isMultipartContent(HttpServletRequest req) { + return ServletFileUpload.isMultipartContent(req); + } + + + // ----------------------------------------------------- Manifest constants + + + /** + * HTTP content type header name. + */ + public static final String CONTENT_TYPE = "Content-type"; + + + /** + * HTTP content disposition header name. + */ + public static final String CONTENT_DISPOSITION = "Content-disposition"; + + + /** + * Content-disposition value for form data. + */ + public static final String FORM_DATA = "form-data"; + + + /** + * Content-disposition value for file attachment. + */ + public static final String ATTACHMENT = "attachment"; + + + /** + * Part of HTTP content type header. + */ + public static final String MULTIPART = "multipart/"; + + + /** + * HTTP content type header for multipart forms. + */ + public static final String MULTIPART_FORM_DATA = "multipart/form-data"; + + + /** + * HTTP content type header for multiple uploads. + */ + public static final String MULTIPART_MIXED = "multipart/mixed"; + + + /** + * The maximum length of a single header line that will be parsed + * (1024 bytes). + * @deprecated This constant is no longer used. As of commons-fileupload + * 1.2, the only applicable limit is the total size of a parts headers, + * {@link MultipartStream#HEADER_PART_SIZE_MAX}. + */ + public static final int MAX_HEADER_SIZE = 1024; + + + // ----------------------------------------------------------- Data members + + + /** + * The maximum size permitted for the complete request, as opposed to + * {@link #fileSizeMax}. A value of -1 indicates no maximum. + */ + private long sizeMax = -1; + + /** + * The maximum size permitted for a single uploaded file, as opposed + * to {@link #sizeMax}. A value of -1 indicates no maximum. + */ + private long fileSizeMax = -1; + + /** + * The content encoding to use when reading part headers. + */ + private String headerEncoding; + + /** + * The progress listener. + */ + private ProgressListener listener; + + // ----------------------------------------------------- Property accessors + + + /** + * Returns the factory class used when creating file items. + * + * @return The factory class for new file items. + */ + public abstract FileItemFactory getFileItemFactory(); + + + /** + * Sets the factory class to use when creating file items. + * + * @param factory The factory class for new file items. + */ + public abstract void setFileItemFactory(FileItemFactory factory); + + + /** + * Returns the maximum allowed size of a complete request, as opposed + * to {@link #getFileSizeMax()}. + * + * @return The maximum allowed size, in bytes. The default value of + * -1 indicates, that there is no limit. + * + * @see #setSizeMax(long) + * + */ + public long getSizeMax() { + return sizeMax; + } + + + /** + * Sets the maximum allowed size of a complete request, as opposed + * to {@link #setFileSizeMax(long)}. + * + * @param sizeMax The maximum allowed size, in bytes. The default value of + * -1 indicates, that there is no limit. + * + * @see #getSizeMax() + * + */ + public void setSizeMax(long sizeMax) { + this.sizeMax = sizeMax; + } + + /** + * Returns the maximum allowed size of a single uploaded file, + * as opposed to {@link #getSizeMax()}. + * + * @see #setFileSizeMax(long) + * @return Maximum size of a single uploaded file. + */ + public long getFileSizeMax() { + return fileSizeMax; + } + + /** + * Sets the maximum allowed size of a single uploaded file, + * as opposed to {@link #getSizeMax()}. + * + * @see #getFileSizeMax() + * @param fileSizeMax Maximum size of a single uploaded file. + */ + public void setFileSizeMax(long fileSizeMax) { + this.fileSizeMax = fileSizeMax; + } + + /** + * Retrieves the character encoding used when reading the headers of an + * individual part. When not specified, or null, the request + * encoding is used. If that is also not specified, or null, + * the platform default encoding is used. + * + * @return The encoding used to read part headers. + */ + public String getHeaderEncoding() { + return headerEncoding; + } + + + /** + * Specifies the character encoding to be used when reading the headers of + * individual part. When not specified, or null, the request + * encoding is used. If that is also not specified, or null, + * the platform default encoding is used. + * + * @param encoding The encoding used to read part headers. + */ + public void setHeaderEncoding(String encoding) { + headerEncoding = encoding; + } + + + // --------------------------------------------------------- Public methods + + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param req The servlet request to be parsed. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * + * @deprecated Use the method in ServletFileUpload instead. + */ + public List /* FileItem */ parseRequest(HttpServletRequest req) + throws FileUploadException { + return parseRequest(new ServletRequestContext(req)); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param ctx The context for the request to be parsed. + * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * @throws IOException An I/O error occurred. This may be a network + * error while communicating with the client or a problem while + * storing the uploaded content. + */ + public FileItemIterator getItemIterator(RequestContext ctx) + throws FileUploadException, IOException { + return new FileItemIteratorImpl(ctx); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param ctx The context for the request to be parsed. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + public List /* FileItem */ parseRequest(RequestContext ctx) + throws FileUploadException { + try { + FileItemIterator iter = getItemIterator(ctx); + List items = new ArrayList(); + FileItemFactory fac = getFileItemFactory(); + if (fac == null) { + throw new NullPointerException( + "No FileItemFactory has been set."); + } + while (iter.hasNext()) { + FileItemStream item = iter.next(); + FileItem fileItem = fac.createItem(item.getFieldName(), + item.getContentType(), item.isFormField(), + item.getName()); + try { + Streams.copy(item.openStream(), fileItem.getOutputStream(), + true); + } catch (FileUploadIOException e) { + throw (FileUploadException) e.getCause(); + } catch (IOException e) { + throw new IOFileUploadException( + "Processing of " + MULTIPART_FORM_DATA + + " request failed. " + e.getMessage(), e); + } + items.add(fileItem); + } + return items; + } catch (FileUploadIOException e) { + throw (FileUploadException) e.getCause(); + } catch (IOException e) { + throw new FileUploadException(e.getMessage(), e); + } + } + + + // ------------------------------------------------------ Protected methods + + + /** + * Retrieves the boundary from the Content-type header. + * + * @param contentType The value of the content type header from which to + * extract the boundary value. + * + * @return The boundary, as a byte array. + */ + protected byte[] getBoundary(String contentType) { + ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + Map params = parser.parse(contentType, ';'); + String boundaryStr = (String) params.get("boundary"); + + if (boundaryStr == null) { + return null; + } + byte[] boundary; + try { + boundary = boundaryStr.getBytes("ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + boundary = boundaryStr.getBytes(); + } + return boundary; + } + + + /** + * Retrieves the file name from the Content-disposition + * header. + * + * @param headers A Map containing the HTTP request headers. + * + * @return The file name for the current encapsulation. + */ + protected String getFileName(Map /* String, String */ headers) { + String fileName = null; + String cd = getHeader(headers, CONTENT_DISPOSITION); + if (cd != null) { + String cdl = cd.toLowerCase(); + if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) { + ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + Map params = parser.parse(cd, ';'); + if (params.containsKey("filename")) { + fileName = (String) params.get("filename"); + if (fileName != null) { + fileName = fileName.trim(); + } else { + // Even if there is no value, the parameter is present, + // so we return an empty file name rather than no file + // name. + fileName = ""; + } + } + } + } + return fileName; + } + + + /** + * Retrieves the field name from the Content-disposition + * header. + * + * @param headers A Map containing the HTTP request headers. + * + * @return The field name for the current encapsulation. + */ + protected String getFieldName(Map /* String, String */ headers) { + String fieldName = null; + String cd = getHeader(headers, CONTENT_DISPOSITION); + if (cd != null && cd.toLowerCase().startsWith(FORM_DATA)) { + + ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + Map params = parser.parse(cd, ';'); + fieldName = (String) params.get("name"); + if (fieldName != null) { + fieldName = fieldName.trim(); + } + } + return fieldName; + } + + + /** + * Creates a new {@link FileItem} instance. + * + * @param headers A Map containing the HTTP request + * headers. + * @param isFormField Whether or not this item is a form field, as + * opposed to a file. + * + * @return A newly created FileItem instance. + * + * @throws FileUploadException if an error occurs. + * @deprecated This method is no longer used in favour of + * internally created instances of {@link FileItem}. + */ + protected FileItem createItem(Map /* String, String */ headers, + boolean isFormField) + throws FileUploadException { + return getFileItemFactory().createItem(getFieldName(headers), + getHeader(headers, CONTENT_TYPE), + isFormField, + getFileName(headers)); + } + + + /** + *

Parses the header-part and returns as key/value + * pairs. + * + *

If there are multiple headers of the same names, the name + * will map to a comma-separated list containing the values. + * + * @param headerPart The header-part of the current + * encapsulation. + * + * @return A Map containing the parsed HTTP request headers. + */ + protected Map /* String, String */ parseHeaders(String headerPart) { + final int len = headerPart.length(); + Map headers = new HashMap(); + int start = 0; + for (;;) { + int end = parseEndOfLine(headerPart, start); + if (start == end) { + break; + } + String header = headerPart.substring(start, end); + start = end + 2; + while (start < len) { + int nonWs = start; + while (nonWs < len) { + char c = headerPart.charAt(nonWs); + if (c != ' ' && c != '\t') { + break; + } + ++nonWs; + } + if (nonWs == start) { + break; + } + // Continuation line found + end = parseEndOfLine(headerPart, nonWs); + header += " " + headerPart.substring(nonWs, end); + start = end + 2; + } + parseHeaderLine(headers, header); + } + return headers; + } + + /** + * Skips bytes until the end of the current line. + * @param headerPart The headers, which are being parsed. + * @param end Index of the last byte, which has yet been + * processed. + * @return Index of the \r\n sequence, which indicates + * end of line. + */ + private int parseEndOfLine(String headerPart, int end) { + int index = end; + for (;;) { + int offset = headerPart.indexOf('\r', index); + if (offset == -1 || offset + 1 >= headerPart.length()) { + throw new IllegalStateException( + "Expected headers to be terminated by an empty line."); + } + if (headerPart.charAt(offset + 1) == '\n') { + return offset; + } + index = offset + 1; + } + } + + /** + * Reads the next header line. + * @param headers String with all headers. + * @param header Map where to store the current header. + */ + private void parseHeaderLine(Map headers, String header) { + final int colonOffset = header.indexOf(':'); + if (colonOffset == -1) { + // This header line is malformed, skip it. + return; + } + String headerName = header.substring(0, colonOffset) + .trim().toLowerCase(); + String headerValue = + header.substring(header.indexOf(':') + 1).trim(); + if (getHeader(headers, headerName) != null) { + // More that one heder of that name exists, + // append to the list. + headers.put(headerName, + getHeader(headers, headerName) + ',' + + headerValue); + } else { + headers.put(headerName, headerValue); + } + } + + /** + * Returns the header with the specified name from the supplied map. The + * header lookup is case-insensitive. + * + * @param headers A Map containing the HTTP request headers. + * @param name The name of the header to return. + * + * @return The value of specified header, or a comma-separated list if + * there were multiple headers of that name. + */ + protected final String getHeader(Map /* String, String */ headers, + String name) { + return (String) headers.get(name.toLowerCase()); + } + + /** + * The iterator, which is returned by + * {@link FileUploadBase#getItemIterator(RequestContext)}. + */ + private class FileItemIteratorImpl implements FileItemIterator { + /** + * Default implementation of {@link FileItemStream}. + */ + private class FileItemStreamImpl implements FileItemStream { + /** The file items content type. + */ + private final String contentType; + /** The file items field name. + */ + private final String fieldName; + /** The file items file name. + */ + private final String name; + /** Whether the file item is a form field. + */ + private final boolean formField; + /** The file items input stream. + */ + private final InputStream stream; + /** Whether the file item was already opened. + */ + private boolean opened; + + /** + * CReates a new instance. + * @param pName The items file name, or null. + * @param pFieldName The items field name. + * @param pContentType The items content type, or null. + * @param pFormField Whether the item is a form field. + */ + FileItemStreamImpl(String pName, String pFieldName, + String pContentType, boolean pFormField) { + name = pName; + fieldName = pFieldName; + contentType = pContentType; + formField = pFormField; + InputStream istream = multi.newInputStream(); + if (fileSizeMax != -1) { + istream = new LimitedInputStream(istream, fileSizeMax) { + protected void raiseError(long pSizeMax, long pCount) + throws IOException { + FileUploadException e = + new FileSizeLimitExceededException( + "The field " + fieldName + + " exceeds its maximum permitted " + + " size of " + pSizeMax + + " characters.", + pCount, pSizeMax); + throw new FileUploadIOException(e); + } + }; + } + stream = istream; + } + + /** + * Returns the items content type, or null. + * @return Content type, if known, or null. + */ + public String getContentType() { + return contentType; + } + + /** + * Returns the items field name. + * @return Field name. + */ + public String getFieldName() { + return fieldName; + } + + /** + * Returns the items file name. + * @return File name, if known, or null. + */ + public String getName() { + return name; + } + + /** + * Returns, whether this is a form field. + * @return True, if the item is a form field, + * otherwise false. + */ + public boolean isFormField() { + return formField; + } + + /** + * Returns an input stream, which may be used to + * read the items contents. + * @return Opened input stream. + * @throws IOException An I/O error occurred. + */ + public InputStream openStream() throws IOException { + if (opened) { + throw new IllegalStateException( + "The stream was already opened."); + } + if (((Closeable) stream).isClosed()) { + throw new FileItemStream.ItemSkippedException(); + } + return stream; + } + + /** + * Closes the file item. + * @throws IOException An I/O error occurred. + */ + void close() throws IOException { + stream.close(); + } + } + + /** + * The multi part stream to process. + */ + private final MultipartStream multi; + /** + * The notifier, which used for triggering the + * {@link ProgressListener}. + */ + private final MultipartStream.ProgressNotifier notifier; + /** + * The boundary, which separates the various parts. + */ + private final byte[] boundary; + /** + * The item, which we currently process. + */ + private FileItemStreamImpl currentItem; + /** + * The current items field name. + */ + private String currentFieldName; + /** + * Whether we are currently skipping the preamble. + */ + private boolean skipPreamble; + /** + * Whether the current item may still be read. + */ + private boolean itemValid; + /** + * Whether we have seen the end of the file. + */ + private boolean eof; + + /** + * Creates a new instance. + * @param ctx The request context. + * @throws FileUploadException An error occurred while + * parsing the request. + * @throws IOException An I/O error occurred. + */ + FileItemIteratorImpl(RequestContext ctx) + throws FileUploadException, IOException { + if (ctx == null) { + throw new NullPointerException("ctx parameter"); + } + + String contentType = ctx.getContentType(); + if ((null == contentType) + || (!contentType.toLowerCase().startsWith(MULTIPART))) { + throw new InvalidContentTypeException( + "the request doesn't contain a " + + MULTIPART_FORM_DATA + + " or " + + MULTIPART_MIXED + + " stream, content type header is " + + contentType); + } + + InputStream input = ctx.getInputStream(); + + if (sizeMax >= 0) { + int requestSize = ctx.getContentLength(); + if (requestSize == -1) { + input = new LimitedInputStream(input, sizeMax) { + protected void raiseError(long pSizeMax, long pCount) + throws IOException { + FileUploadException ex = + new SizeLimitExceededException( + "the request was rejected because" + + " its size (" + pCount + + ") exceeds the configured maximum" + + " (" + pSizeMax + ")", + pCount, pSizeMax); + throw new FileUploadIOException(ex); + } + }; + } else { + if (sizeMax >= 0 && requestSize > sizeMax) { + throw new SizeLimitExceededException( + "the request was rejected because its size (" + + requestSize + + ") exceeds the configured maximum (" + + sizeMax + ")", + requestSize, sizeMax); + } + } + } + + String charEncoding = headerEncoding; + if (charEncoding == null) { + charEncoding = ctx.getCharacterEncoding(); + } + + boundary = getBoundary(contentType); + if (boundary == null) { + throw new FileUploadException( + "the request was rejected because " + + "no multipart boundary was found"); + } + + notifier = new MultipartStream.ProgressNotifier(listener, + ctx.getContentLength()); + multi = new MultipartStream(input, boundary, notifier); + multi.setHeaderEncoding(charEncoding); + + skipPreamble = true; + findNextItem(); + } + + /** + * Called for finding the nex item, if any. + * @return True, if an next item was found, otherwise false. + * @throws IOException An I/O error occurred. + */ + private boolean findNextItem() throws IOException { + if (eof) { + return false; + } + if (currentItem != null) { + currentItem.close(); + currentItem = null; + } + for (;;) { + boolean nextPart; + if (skipPreamble) { + nextPart = multi.skipPreamble(); + } else { + nextPart = multi.readBoundary(); + } + if (!nextPart) { + if (currentFieldName == null) { + // Outer multipart terminated -> No more data + eof = true; + return false; + } + // Inner multipart terminated -> Return to parsing the outer + multi.setBoundary(boundary); + currentFieldName = null; + continue; + } + Map headers = parseHeaders(multi.readHeaders()); + if (currentFieldName == null) { + // We're parsing the outer multipart + String fieldName = getFieldName(headers); + if (fieldName != null) { + String subContentType + = getHeader(headers, CONTENT_TYPE); + if (subContentType != null + && subContentType.toLowerCase() + .startsWith(MULTIPART_MIXED)) { + currentFieldName = fieldName; + // Multiple files associated with this field name + byte[] subBoundary = getBoundary(subContentType); + multi.setBoundary(subBoundary); + skipPreamble = true; + continue; + } + String fileName = getFileName(headers); + currentItem = new FileItemStreamImpl(fileName, + fieldName, getHeader(headers, CONTENT_TYPE), + fileName == null); + notifier.noteItem(); + itemValid = true; + return true; + } + } else { + String fileName = getFileName(headers); + if (fileName != null) { + currentItem = new FileItemStreamImpl(fileName, + currentFieldName, + getHeader(headers, CONTENT_TYPE), + false); + notifier.noteItem(); + itemValid = true; + return true; + } + } + multi.discardBodyData(); + } + } + + /** + * Returns, whether another instance of {@link FileItemStream} + * is available. + * @throws FileUploadException Parsing or processing the + * file item failed. + * @throws IOException Reading the file item failed. + * @return True, if one or more additional file items + * are available, otherwise false. + */ + public boolean hasNext() throws FileUploadException, IOException { + if (eof) { + return false; + } + if (itemValid) { + return true; + } + return findNextItem(); + } + + /** + * Returns the next available {@link FileItemStream}. + * @throws java.util.NoSuchElementException No more items are + * available. Use {@link #hasNext()} to prevent this exception. + * @throws FileUploadException Parsing or processing the + * file item failed. + * @throws IOException Reading the file item failed. + * @return FileItemStream instance, which provides + * access to the next file item. + */ + public FileItemStream next() throws FileUploadException, IOException { + if (eof || (!itemValid && !hasNext())) { + throw new NoSuchElementException(); + } + itemValid = false; + return currentItem; + } + } + + /** + * This exception is thrown for hiding an inner + * {@link FileUploadException} in an {@link IOException}. + */ + public static class FileUploadIOException extends IOException { + /** The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = -7047616958165584154L; + /** The exceptions cause; we overwrite the parent + * classes field, which is available since Java + * 1.4 only. + */ + private final FileUploadException cause; + + /** + * Creates a FileUploadIOException with the + * given cause. + * @param pCause The exceptions cause, if any, or null. + */ + public FileUploadIOException(FileUploadException pCause) { + // We're not doing super(pCause) cause of 1.3 compatibility. + cause = pCause; + } + + /** + * Returns the exceptions cause. + * @return The exceptions cause, if any, or null. + */ + public Throwable getCause() { + return cause; + } + } + + /** + * Thrown to indicate that the request is not a multipart request. + */ + public static class InvalidContentTypeException + extends FileUploadException { + /** The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = -9073026332015646668L; + + /** + * Constructs a InvalidContentTypeException with no + * detail message. + */ + public InvalidContentTypeException() { + // Nothing to do. + } + + /** + * Constructs an InvalidContentTypeException with + * the specified detail message. + * + * @param message The detail message. + */ + public InvalidContentTypeException(String message) { + super(message); + } + } + + /** + * Thrown to indicate an IOException. + */ + public static class IOFileUploadException extends FileUploadException { + /** The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = 1749796615868477269L; + /** The exceptions cause; we overwrite the parent + * classes field, which is available since Java + * 1.4 only. + */ + private final IOException cause; + + /** + * Creates a new instance with the given cause. + * @param pMsg The detail message. + * @param pException The exceptions cause. + */ + public IOFileUploadException(String pMsg, IOException pException) { + super(pMsg); + cause = pException; + } + + /** + * Returns the exceptions cause. + * @return The exceptions cause, if any, or null. + */ + public Throwable getCause() { + return cause; + } + } + + /** This exception is thrown, if a requests permitted size + * is exceeded. + */ + protected abstract static class SizeException extends FileUploadException { + /** + * The actual size of the request. + */ + private final long actual; + + /** + * The maximum permitted size of the request. + */ + private final long permitted; + + /** + * Creates a new instance. + * @param message The detail message. + * @param actual The actual number of bytes in the request. + * @param permitted The requests size limit, in bytes. + */ + protected SizeException(String message, long actual, long permitted) { + super(message); + this.actual = actual; + this.permitted = permitted; + } + + /** + * Retrieves the actual size of the request. + * + * @return The actual size of the request. + */ + public long getActualSize() { + return actual; + } + + /** + * Retrieves the permitted size of the request. + * + * @return The permitted size of the request. + */ + public long getPermittedSize() { + return permitted; + } + } + + /** + * Thrown to indicate that the request size is not specified. In other + * words, it is thrown, if the content-length header is missing or + * contains the value -1. + * @deprecated As of commons-fileupload 1.2, the presence of a + * content-length header is no longer required. + */ + public static class UnknownSizeException + extends FileUploadException { + /** The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = 7062279004812015273L; + + /** + * Constructs a UnknownSizeException with no + * detail message. + */ + public UnknownSizeException() { + super(); + } + + /** + * Constructs an UnknownSizeException with + * the specified detail message. + * + * @param message The detail message. + */ + public UnknownSizeException(String message) { + super(message); + } + } + + /** + * Thrown to indicate that the request size exceeds the configured maximum. + */ + public static class SizeLimitExceededException + extends SizeException { + /** The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = -2474893167098052828L; + + /** + * @deprecated Replaced by + * {@link #SizeLimitExceededException(String, long, long)} + */ + public SizeLimitExceededException() { + this(null, 0, 0); + } + + /** + * @deprecated Replaced by + * {@link #SizeLimitExceededException(String, long, long)} + * @param message The exceptions detail message. + */ + public SizeLimitExceededException(String message) { + this(message, 0, 0); + } + + /** + * Constructs a SizeExceededException with + * the specified detail message, and actual and permitted sizes. + * + * @param message The detail message. + * @param actual The actual request size. + * @param permitted The maximum permitted request size. + */ + public SizeLimitExceededException(String message, long actual, + long permitted) { + super(message, actual, permitted); + } + } + + /** + * Thrown to indicate that A files size exceeds the configured maximum. + */ + public static class FileSizeLimitExceededException + extends SizeException { + /** The exceptions UID, for serializing an instance. + */ + private static final long serialVersionUID = 8150776562029630058L; + + /** + * Constructs a SizeExceededException with + * the specified detail message, and actual and permitted sizes. + * + * @param message The detail message. + * @param actual The actual request size. + * @param permitted The maximum permitted request size. + */ + public FileSizeLimitExceededException(String message, long actual, + long permitted) { + super(message, actual, permitted); + } + } + + /** + * Returns the progress listener. + * @return The progress listener, if any, or null. + */ + public ProgressListener getProgressListener() { + return listener; + } + + /** + * Sets the progress listener. + * @param pListener The progress listener, if any. Defaults to null. + */ + public void setProgressListener(ProgressListener pListener) { + listener = pListener; + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUploadException.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUploadException.java new file mode 100755 index 000000000..2e5b1b9e4 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/FileUploadException.java @@ -0,0 +1,95 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.PrintStream; +import java.io.PrintWriter; + + +/** + * Exception for errors encountered while processing the request. + * + * @author John McNally + * @version $Id$ + */ +public class FileUploadException extends Exception { + /** + * Serial version UID, being used, if the exception + * is serialized. + */ + private static final long serialVersionUID = 8881893724388807504L; + /** + * The exceptions cause. We overwrite the cause of + * the super class, which isn't available in Java 1.3. + */ + private final Throwable cause; + + /** + * Constructs a new FileUploadException without message. + */ + public FileUploadException() { + this(null, null); + } + + /** + * Constructs a new FileUploadException with specified detail + * message. + * + * @param msg the error message. + */ + public FileUploadException(final String msg) { + this(msg, null); + } + + /** + * Creates a new FileUploadException with the given + * detail message and cause. + * @param msg The exceptions detail message. + * @param cause The exceptions cause. + */ + public FileUploadException(String msg, Throwable cause) { + super(msg); + this.cause = cause; + } + + /** + * Prints this throwable and its backtrace to the specified print stream. + * + * @param stream PrintStream to use for output + */ + public void printStackTrace(PrintStream stream) { + super.printStackTrace(stream); + if (cause != null) { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + /** + * Prints this throwable and its backtrace to the specified + * print writer. + * + * @param writer PrintWriter to use for output + */ + public void printStackTrace(PrintWriter writer) { + super.printStackTrace(writer); + if (cause != null) { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/MultipartStream.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/MultipartStream.java new file mode 100755 index 000000000..6ea2bbd6e --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/MultipartStream.java @@ -0,0 +1,1047 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import com.fr.third.org.apache.commons.fileupload.util.Closeable; +import com.fr.third.org.apache.commons.fileupload.util.Streams; + +/** + *

Low level API for processing file uploads. + * + *

This class can be used to process data streams conforming to MIME + * 'multipart' format as defined in + * RFC 1867. Arbitrarily + * large amounts of data in the stream can be processed under constant + * memory usage. + * + *

The format of the stream is defined in the following way:
+ * + * + * multipart-body := preamble 1*encapsulation close-delimiter epilogue
+ * encapsulation := delimiter body CRLF
+ * delimiter := "--" boundary CRLF
+ * close-delimiter := "--" boudary "--"
+ * preamble := <ignore>
+ * epilogue := <ignore>
+ * body := header-part CRLF body-part
+ * header-part := 1*header CRLF
+ * header := header-name ":" header-value
+ * header-name := <printable ascii characters except ":">
+ * header-value := <any ascii characters except CR & LF>
+ * body-data := <arbitrary data>
+ *
+ * + *

Note that body-data can contain another mulipart entity. There + * is limited support for single pass processing of such nested + * streams. The nested stream is required to have a + * boundary token of the same length as the parent stream (see {@link + * #setBoundary(byte[])}). + * + *

Here is an example of usage of this class.
+ * + *

+ *    try {
+ *        MultipartStream multipartStream = new MultipartStream(input,
+ *                                                              boundary);
+ *        boolean nextPart = multipartStream.skipPreamble();
+ *        OutputStream output;
+ *        while(nextPart) {
+ *            header = chunks.readHeader();
+ *            // process headers
+ *            // create some output stream
+ *            multipartStream.readBodyPart(output);
+ *            nextPart = multipartStream.readBoundary();
+ *        }
+ *    } catch(MultipartStream.MalformedStreamException e) {
+ *          // the stream failed to follow required syntax
+ *    } catch(IOException) {
+ *          // a read or write error occurred
+ *    }
+ *
+ * 
+ * + * @author Rafal Krzewski + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id$ + */ +public class MultipartStream { + /** + * Internal class, which is used to invoke the + * {@link ProgressListener}. + */ + static class ProgressNotifier { + /** The listener to invoke. + */ + private final ProgressListener listener; + /** Number of expected bytes, if known, or -1. + */ + private final long contentLength; + /** Number of bytes, which have been read so far. + */ + private long bytesRead; + /** Number of items, which have been read so far. + */ + private int items; + /** Creates a new instance with the given listener + * and content length. + * @param pListener The listener to invoke. + * @param pContentLength The expected content length. + */ + ProgressNotifier(ProgressListener pListener, long pContentLength) { + listener = pListener; + contentLength = pContentLength; + } + /** Called to indicate that bytes have been read. + * @param pBytes Number of bytes, which have been read. + */ + void noteBytesRead(int pBytes) { + /* Indicates, that the given number of bytes have been read from + * the input stream. + */ + bytesRead += pBytes; + notifyListener(); + } + /** Called to indicate, that a new file item has been detected. + */ + void noteItem() { + ++items; + } + /** Called for notifying the listener. + */ + private void notifyListener() { + if (listener != null) { + listener.update(bytesRead, contentLength, items); + } + } + } + + // ----------------------------------------------------- Manifest constants + + + /** + * The Carriage Return ASCII character value. + */ + public static final byte CR = 0x0D; + + + /** + * The Line Feed ASCII character value. + */ + public static final byte LF = 0x0A; + + + /** + * The dash (-) ASCII character value. + */ + public static final byte DASH = 0x2D; + + + /** + * The maximum length of header-part that will be + * processed (10 kilobytes = 10240 bytes.). + */ + public static final int HEADER_PART_SIZE_MAX = 10240; + + + /** + * The default length of the buffer used for processing a request. + */ + protected static final int DEFAULT_BUFSIZE = 4096; + + + /** + * A byte sequence that marks the end of header-part + * (CRLFCRLF). + */ + protected static final byte[] HEADER_SEPARATOR = { + CR, LF, CR, LF }; + + + /** + * A byte sequence that that follows a delimiter that will be + * followed by an encapsulation (CRLF). + */ + protected static final byte[] FIELD_SEPARATOR = { + CR, LF}; + + + /** + * A byte sequence that that follows a delimiter of the last + * encapsulation in the stream (--). + */ + protected static final byte[] STREAM_TERMINATOR = { + DASH, DASH}; + + + /** + * A byte sequence that precedes a boundary (CRLF--). + */ + protected static final byte[] BOUNDARY_PREFIX = { + CR, LF, DASH, DASH}; + + + /** + * The number of bytes, over and above the boundary size, to use for the + * keep region. + */ + private static final int KEEP_REGION_PAD = 3; + + + // ----------------------------------------------------------- Data members + + + /** + * The input stream from which data is read. + */ + private final InputStream input; + + + /** + * The length of the boundary token plus the leading CRLF--. + */ + private int boundaryLength; + + + /** + * The amount of data, in bytes, that must be kept in the buffer in order + * to detect delimiters reliably. + */ + private int keepRegion; + + + /** + * The byte sequence that partitions the stream. + */ + private byte[] boundary; + + + /** + * The length of the buffer used for processing the request. + */ + private final int bufSize; + + + /** + * The buffer used for processing the request. + */ + private final byte[] buffer; + + + /** + * The index of first valid character in the buffer. + *
+ * 0 <= head < bufSize + */ + private int head; + + + /** + * The index of last valid characer in the buffer + 1. + *
+ * 0 <= tail <= bufSize + */ + private int tail; + + + /** + * The content encoding to use when reading headers. + */ + private String headerEncoding; + + + /** + * The progress notifier, if any, or null. + */ + private final ProgressNotifier notifier; + + // ----------------------------------------------------------- Constructors + + /** + * Creates a new instance. + * @deprecated Use {@link #MultipartStream(InputStream, byte[], + * com.fr.third.org.apache.commons.fileupload.MultipartStream.ProgressNotifier)}, + * or {@link #MultipartStream(InputStream, byte[], int, + * com.fr.third.org.apache.commons.fileupload.MultipartStream.ProgressNotifier)} + */ + public MultipartStream() { + this(null, null, null); + } + + /** + *

Constructs a MultipartStream with a custom size buffer + * and no progress notifier. + * + *

Note that the buffer must be at least big enough to contain the + * boundary string, plus 4 characters for CR/LF and double dash, plus at + * least one byte of data. Too small a buffer size setting will degrade + * performance. + * + * @param input The InputStream to serve as a data source. + * @param boundary The token used for dividing the stream into + * encapsulations. + * @param bufSize The size of the buffer to be used, in bytes. + * + * @see #MultipartStream(InputStream, byte[], ProgressNotifier) + * @deprecated Use {@link #MultipartStream(InputStream, byte[], int, + * com.fr.third.org.apache.commons.fileupload.MultipartStream.ProgressNotifier)}. + */ + public MultipartStream(InputStream input, byte[] boundary, int bufSize) { + this(input, boundary, bufSize, null); + } + + /** + *

Constructs a MultipartStream with a custom size buffer. + * + *

Note that the buffer must be at least big enough to contain the + * boundary string, plus 4 characters for CR/LF and double dash, plus at + * least one byte of data. Too small a buffer size setting will degrade + * performance. + * + * @param input The InputStream to serve as a data source. + * @param boundary The token used for dividing the stream into + * encapsulations. + * @param bufSize The size of the buffer to be used, in bytes. + * @param pNotifier The notifier, which is used for calling the + * progress listener, if any. + * + * @see #MultipartStream(InputStream, byte[], ProgressNotifier) + */ + MultipartStream(InputStream input, + byte[] boundary, + int bufSize, + ProgressNotifier pNotifier) { + this.input = input; + this.bufSize = bufSize; + this.buffer = new byte[bufSize]; + this.notifier = pNotifier; + + // We prepend CR/LF to the boundary to chop trailng CR/LF from + // body-data tokens. + this.boundary = new byte[boundary.length + BOUNDARY_PREFIX.length]; + this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length; + this.keepRegion = boundary.length + KEEP_REGION_PAD; + System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, + BOUNDARY_PREFIX.length); + System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, + boundary.length); + + head = 0; + tail = 0; + } + + + /** + *

Constructs a MultipartStream with a default size buffer. + * + * @param input The InputStream to serve as a data source. + * @param boundary The token used for dividing the stream into + * encapsulations. + * @param pNotifier An object for calling the progress listener, if any. + * + * + * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier) + */ + MultipartStream(InputStream input, + byte[] boundary, + ProgressNotifier pNotifier) { + this(input, boundary, DEFAULT_BUFSIZE, pNotifier); + } + + /** + *

Constructs a MultipartStream with a default size buffer. + * + * @param input The InputStream to serve as a data source. + * @param boundary The token used for dividing the stream into + * encapsulations. + * + * @deprecated Use {@link #MultipartStream(InputStream, byte[], + * ProgressNotifier)}. + * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier) + */ + public MultipartStream(InputStream input, + byte[] boundary) { + this(input, boundary, DEFAULT_BUFSIZE, null); + } + + // --------------------------------------------------------- Public methods + + + /** + * Retrieves the character encoding used when reading the headers of an + * individual part. When not specified, or null, the platform + * default encoding is used. + + * + * @return The encoding used to read part headers. + */ + public String getHeaderEncoding() { + return headerEncoding; + } + + + /** + * Specifies the character encoding to be used when reading the headers of + * individual parts. When not specified, or null, the platform + * default encoding is used. + * + * @param encoding The encoding used to read part headers. + */ + public void setHeaderEncoding(String encoding) { + headerEncoding = encoding; + } + + + /** + * Reads a byte from the buffer, and refills it as + * necessary. + * + * @return The next byte from the input stream. + * + * @throws IOException if there is no more data available. + */ + public byte readByte() + throws IOException { + // Buffer depleted ? + if (head == tail) { + head = 0; + // Refill. + tail = input.read(buffer, head, bufSize); + if (tail == -1) { + // No more data available. + throw new IOException("No more data is available"); + } + notifier.noteBytesRead(tail); + } + return buffer[head++]; + } + + + /** + * Skips a boundary token, and checks whether more + * encapsulations are contained in the stream. + * + * @return true if there are more encapsulations in + * this stream; false otherwise. + * + * @throws MalformedStreamException if the stream ends unexpecetedly or + * fails to follow required syntax. + */ + public boolean readBoundary() + throws MalformedStreamException { + byte[] marker = new byte[2]; + boolean nextChunk = false; + + head += boundaryLength; + try { + marker[0] = readByte(); + if (marker[0] == LF) { + // Work around IE5 Mac bug with input type=image. + // Because the boundary delimiter, not including the trailing + // CRLF, must not appear within any file (RFC 2046, section + // 5.1.1), we know the missing CR is due to a buggy browser + // rather than a file containing something similar to a + // boundary. + return true; + } + + marker[1] = readByte(); + if (arrayequals(marker, STREAM_TERMINATOR, 2)) { + nextChunk = false; + } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) { + nextChunk = true; + } else { + throw new MalformedStreamException( + "Unexpected characters follow a boundary"); + } + } catch (IOException e) { + throw new MalformedStreamException("Stream ended unexpectedly"); + } + return nextChunk; + } + + + /** + *

Changes the boundary token used for partitioning the stream. + * + *

This method allows single pass processing of nested multipart + * streams. + * + *

The boundary token of the nested stream is required + * to be of the same length as the boundary token in parent stream. + * + *

Restoring the parent stream boundary token after processing of a + * nested stream is left to the application. + * + * @param boundary The boundary to be used for parsing of the nested + * stream. + * + * @throws IllegalBoundaryException if the boundary + * has a different length than the one + * being currently parsed. + */ + public void setBoundary(byte[] boundary) + throws IllegalBoundaryException { + if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) { + throw new IllegalBoundaryException( + "The length of a boundary token can not be changed"); + } + System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, + boundary.length); + } + + + /** + *

Reads the header-part of the current + * encapsulation. + * + *

Headers are returned verbatim to the input stream, including the + * trailing CRLF marker. Parsing is left to the + * application. + * + *

TODO allow limiting maximum header size to + * protect against abuse. + * + * @return The header-part of the current encapsulation. + * + * @throws MalformedStreamException if the stream ends unexpecetedly. + */ + public String readHeaders() + throws MalformedStreamException { + int i = 0; + byte[] b = new byte[1]; + // to support multi-byte characters + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int sizeMax = HEADER_PART_SIZE_MAX; + int size = 0; + while (i < HEADER_SEPARATOR.length) { + try { + b[0] = readByte(); + } catch (IOException e) { + throw new MalformedStreamException("Stream ended unexpectedly"); + } + size++; + if (b[0] == HEADER_SEPARATOR[i]) { + i++; + } else { + i = 0; + } + if (size <= sizeMax) { + baos.write(b[0]); + } + } + + String headers = null; + if (headerEncoding != null) { + try { + headers = baos.toString(headerEncoding); + } catch (UnsupportedEncodingException e) { + // Fall back to platform default if specified encoding is not + // supported. + headers = baos.toString(); + } + } else { + headers = baos.toString(); + } + + return headers; + } + + + /** + *

Reads body-data from the current + * encapsulation and writes its contents into the + * output Stream. + * + *

Arbitrary large amounts of data can be processed by this + * method using a constant size buffer. (see {@link + * #MultipartStream(InputStream,byte[],int, ProgressNotifier) constructor}). + * + * @param output The Stream to write data into. May + * be null, in which case this method is equivalent + * to {@link #discardBodyData()}. + * + * @return the amount of data written. + * + * @throws MalformedStreamException if the stream ends unexpectedly. + * @throws IOException if an i/o error occurs. + */ + public int readBodyData(OutputStream output) + throws MalformedStreamException, IOException { + final InputStream istream = newInputStream(); + return (int) Streams.copy(istream, output, false); + } + + /** + * Creates a new {@link ItemInputStream}. + * @return A new instance of {@link ItemInputStream}. + */ + ItemInputStream newInputStream() { + return new ItemInputStream(); + } + + /** + *

Reads body-data from the current + * encapsulation and discards it. + * + *

Use this method to skip encapsulations you don't need or don't + * understand. + * + * @return The amount of data discarded. + * + * @throws MalformedStreamException if the stream ends unexpectedly. + * @throws IOException if an i/o error occurs. + */ + public int discardBodyData() + throws MalformedStreamException, + IOException { + return readBodyData(null); + } + + + /** + * Finds the beginning of the first encapsulation. + * + * @return true if an encapsulation was found in + * the stream. + * + * @throws IOException if an i/o error occurs. + */ + public boolean skipPreamble() + throws IOException { + // First delimiter may be not preceeded with a CRLF. + System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); + boundaryLength = boundary.length - 2; + try { + // Discard all data up to the delimiter. + discardBodyData(); + + // Read boundary - if succeded, the stream contains an + // encapsulation. + return readBoundary(); + } catch (MalformedStreamException e) { + return false; + } finally { + // Restore delimiter. + System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2); + boundaryLength = boundary.length; + boundary[0] = CR; + boundary[1] = LF; + } + } + + + /** + * Compares count first bytes in the arrays + * a and b. + * + * @param a The first array to compare. + * @param b The second array to compare. + * @param count How many bytes should be compared. + * + * @return true if count first bytes in arrays + * a and b are equal. + */ + public static boolean arrayequals(byte[] a, + byte[] b, + int count) { + for (int i = 0; i < count; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + + + /** + * Searches for a byte of specified value in the buffer, + * starting at the specified position. + * + * @param value The value to find. + * @param pos The starting position for searching. + * + * @return The position of byte found, counting from beginning of the + * buffer, or -1 if not found. + */ + protected int findByte(byte value, + int pos) { + for (int i = pos; i < tail; i++) { + if (buffer[i] == value) { + return i; + } + } + + return -1; + } + + + /** + * Searches for the boundary in the buffer + * region delimited by head and tail. + * + * @return The position of the boundary found, counting from the + * beginning of the buffer, or -1 if + * not found. + */ + protected int findSeparator() { + int first; + int match = 0; + int maxpos = tail - boundaryLength; + for (first = head; + (first <= maxpos) && (match != boundaryLength); + first++) { + first = findByte(boundary[0], first); + if (first == -1 || (first > maxpos)) { + return -1; + } + for (match = 1; match < boundaryLength; match++) { + if (buffer[first + match] != boundary[match]) { + break; + } + } + } + if (match == boundaryLength) { + return first - 1; + } + return -1; + } + + /** + * Thrown to indicate that the input stream fails to follow the + * required syntax. + */ + public static class MalformedStreamException + extends IOException { + /** + * Constructs a MalformedStreamException with no + * detail message. + */ + public MalformedStreamException() { + super(); + } + + /** + * Constructs an MalformedStreamException with + * the specified detail message. + * + * @param message The detail message. + */ + public MalformedStreamException(String message) { + super(message); + } + } + + + /** + * Thrown upon attempt of setting an invalid boundary token. + */ + public static class IllegalBoundaryException + extends IOException { + /** + * Constructs an IllegalBoundaryException with no + * detail message. + */ + public IllegalBoundaryException() { + super(); + } + + /** + * Constructs an IllegalBoundaryException with + * the specified detail message. + * + * @param message The detail message. + */ + public IllegalBoundaryException(String message) { + super(message); + } + } + + /** + * An {@link InputStream} for reading an items contents. + */ + public class ItemInputStream extends InputStream implements Closeable { + /** The number of bytes, which have been read so far. + */ + private long total; + /** The number of bytes, which must be hold, because + * they might be a part of the boundary. + */ + private int pad; + /** The current offset in the buffer. + */ + private int pos; + /** Whether the stream is already closed. + */ + private boolean closed; + + /** + * Creates a new instance. + */ + ItemInputStream() { + findSeparator(); + } + + /** + * Called for finding the separator. + */ + private void findSeparator() { + pos = MultipartStream.this.findSeparator(); + if (pos == -1) { + if (tail - head > keepRegion) { + pad = keepRegion; + } else { + pad = tail - head; + } + } + } + + /** + * Returns the number of bytes, which have been read + * by the stream. + * @return Number of bytes, which have been read so far. + */ + public long getBytesRead() { + return total; + } + + /** + * Returns the number of bytes, which are currently + * available, without blocking. + * @throws IOException An I/O error occurs. + * @return Number of bytes in the buffer. + */ + public int available() throws IOException { + if (pos == -1) { + return tail - head - pad; + } + return pos - head; + } + + /** Offset when converting negative bytes to integers. + */ + private static final int BYTE_POSITIVE_OFFSET = 256; + + /** + * Returns the next byte in the stream. + * @return The next byte in the stream, as a non-negative + * integer, or -1 for EOF. + * @throws IOException An I/O error occurred. + */ + public int read() throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + if (available() == 0) { + if (makeAvailable() == 0) { + return -1; + } + } + ++total; + int b = buffer[head++]; + if (b >= 0) { + return b; + } + return b + BYTE_POSITIVE_OFFSET; + } + + /** + * Reads bytes into the given buffer. + * @param b The destination buffer, where to write to. + * @param off Offset of the first byte in the buffer. + * @param len Maximum number of bytes to read. + * @return Number of bytes, which have been actually read, + * or -1 for EOF. + * @throws IOException An I/O error occurred. + */ + public int read(byte[] b, int off, int len) throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + if (len == 0) { + return 0; + } + int res = available(); + if (res == 0) { + res = makeAvailable(); + if (res == 0) { + return -1; + } + } + res = Math.min(res, len); + System.arraycopy(buffer, head, b, off, res); + head += res; + total += res; + return res; + } + + /** + * Closes the input stream. + * @throws IOException An I/O error occurred. + */ + public void close() throws IOException { + if (closed) { + return; + } + for (;;) { + int av = available(); + if (av == 0) { + av = makeAvailable(); + if (av == 0) { + break; + } + } + skip(av); + } + closed = true; + } + + /** + * Skips the given number of bytes. + * @param bytes Number of bytes to skip. + * @return The number of bytes, which have actually been + * skipped. + * @throws IOException An I/O error occurred. + */ + public long skip(long bytes) throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + int av = available(); + if (av == 0) { + av = makeAvailable(); + if (av == 0) { + return 0; + } + } + long res = Math.min(av, bytes); + head += res; + return res; + } + + /** + * Attempts to read more data. + * @return Number of available bytes + * @throws IOException An I/O error occurred. + */ + private int makeAvailable() throws IOException { + if (pos != -1) { + return 0; + } + + // Move the data to the beginning of the buffer. + total += tail - head - pad; + System.arraycopy(buffer, tail - pad, buffer, 0, pad); + + // Refill buffer with new data. + head = 0; + int bytesRead = input.read(buffer, pad, bufSize - pad); + if (bytesRead == -1) { + // The last pad amount is left in the buffer. + // Boundary can't be in there so signal an error + // condition. + throw new MalformedStreamException( + "Stream ended unexpectedly"); + } + notifier.noteBytesRead(bytesRead); + tail = pad + bytesRead; + findSeparator(); + return available(); + } + + /** + * Returns, whether the stream is closed. + * @return True, if the stream is closed, otherwise false. + */ + public boolean isClosed() { + return closed; + } + } + + // ------------------------------------------------------ Debugging methods + + + // These are the methods that were used to debug this stuff. + /* + + // Dump data. + protected void dump() + { + System.out.println("01234567890"); + byte[] temp = new byte[buffer.length]; + for(int i=0; i i1) { + result = new String(chars, i1, i2 - i1); + } + return result; + } + + /** + * Tests if the given character is present in the array of characters. + * + * @param ch the character to test for presense in the array of characters + * @param charray the array of characters to test against + * + * @return true if the character is present in the array of + * characters, false otherwise. + */ + private boolean isOneOf(char ch, final char[] charray) { + boolean result = false; + for (int i = 0; i < charray.length; i++) { + if (ch == charray[i]) { + result = true; + break; + } + } + return result; + } + + /** + * Parses out a token until any of the given terminators + * is encountered. + * + * @param terminators the array of terminating characters. Any of these + * characters when encountered signify the end of the token + * + * @return the token + */ + private String parseToken(final char[] terminators) { + char ch; + i1 = pos; + i2 = pos; + while (hasChar()) { + ch = chars[pos]; + if (isOneOf(ch, terminators)) { + break; + } + i2++; + pos++; + } + return getToken(false); + } + + /** + * Parses out a token until any of the given terminators + * is encountered outside the quotation marks. + * + * @param terminators the array of terminating characters. Any of these + * characters when encountered outside the quotation marks signify the end + * of the token + * + * @return the token + */ + private String parseQuotedToken(final char[] terminators) { + char ch; + i1 = pos; + i2 = pos; + boolean quoted = false; + boolean charEscaped = false; + while (hasChar()) { + ch = chars[pos]; + if (!quoted && isOneOf(ch, terminators)) { + break; + } + if (!charEscaped && ch == '"') { + quoted = !quoted; + } + charEscaped = (!charEscaped && ch == '\\'); + i2++; + pos++; + + } + return getToken(true); + } + + /** + * Returns true if parameter names are to be converted to lower + * case when name/value pairs are parsed. + * + * @return true if parameter names are to be + * converted to lower case when name/value pairs are parsed. + * Otherwise returns false + */ + public boolean isLowerCaseNames() { + return this.lowerCaseNames; + } + + /** + * Sets the flag if parameter names are to be converted to lower case when + * name/value pairs are parsed. + * + * @param b true if parameter names are to be + * converted to lower case when name/value pairs are parsed. + * false otherwise. + */ + public void setLowerCaseNames(boolean b) { + this.lowerCaseNames = b; + } + + /** + * Extracts a map of name/value pairs from the given string. Names are + * expected to be unique. + * + * @param str the string that contains a sequence of name/value pairs + * @param separator the name/value pairs separator + * + * @return a map of name/value pairs + */ + public Map parse(final String str, char separator) { + if (str == null) { + return new HashMap(); + } + return parse(str.toCharArray(), separator); + } + + /** + * Extracts a map of name/value pairs from the given array of + * characters. Names are expected to be unique. + * + * @param chars the array of characters that contains a sequence of + * name/value pairs + * @param separator the name/value pairs separator + * + * @return a map of name/value pairs + */ + public Map parse(final char[] chars, char separator) { + if (chars == null) { + return new HashMap(); + } + return parse(chars, 0, chars.length, separator); + } + + /** + * Extracts a map of name/value pairs from the given array of + * characters. Names are expected to be unique. + * + * @param chars the array of characters that contains a sequence of + * name/value pairs + * @param offset - the initial offset. + * @param length - the length. + * @param separator the name/value pairs separator + * + * @return a map of name/value pairs + */ + public Map parse( + final char[] chars, + int offset, + int length, + char separator) { + + if (chars == null) { + return new HashMap(); + } + HashMap params = new HashMap(); + this.chars = chars; + this.pos = offset; + this.len = length; + + String paramName = null; + String paramValue = null; + while (hasChar()) { + paramName = parseToken(new char[] { + '=', separator }); + paramValue = null; + if (hasChar() && (chars[pos] == '=')) { + pos++; // skip '=' + paramValue = parseQuotedToken(new char[] { + separator }); + } + if (hasChar() && (chars[pos] == separator)) { + pos++; // skip separator + } + if ((paramName != null) && (paramName.length() > 0)) { + if (this.lowerCaseNames) { + paramName = paramName.toLowerCase(); + } + params.put(paramName, paramValue); + } + } + return params; + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/ProgressListener.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/ProgressListener.java new file mode 100755 index 000000000..90501427e --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/ProgressListener.java @@ -0,0 +1,34 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + + +/** + * The {@link ProgressListener} may be used to display a progress bar + * or do stuff like that. + */ +public interface ProgressListener { + /** Updates the listeners status information. + * @param pBytesRead The total number of bytes, which have been read + * so far. + * @param pContentLength The total number of bytes, which are being + * read. May be -1, if this number is unknown. + * @param pItems The number of the field, which is currently being + * read. (0 = no item so far, 1 = first item is being read, ...) + */ + void update(long pBytesRead, long pContentLength, int pItems); +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/RequestContext.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/RequestContext.java new file mode 100755 index 000000000..6056e7390 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/RequestContext.java @@ -0,0 +1,64 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload; + +import java.io.InputStream; +import java.io.IOException; + +/** + *

Abstracts access to the request information needed for file uploads. This + * interfsace should be implemented for each type of request that may be + * handled by FileUpload, such as servlets and portlets.

+ * + * @author Martin Cooper + * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public interface RequestContext { + + /** + * Retrieve the character encoding for the request. + * + * @return The character encoding for the request. + */ + String getCharacterEncoding(); + + /** + * Retrieve the content type of the request. + * + * @return The content type of the request. + */ + String getContentType(); + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + */ + int getContentLength(); + + /** + * Retrieve the input stream for the request. + * + * @return The input stream for the request. + * + * @throws IOException if a problem occurs. + */ + InputStream getInputStream() throws IOException; +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/DiskFileItem.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/DiskFileItem.java new file mode 100755 index 000000000..2bd3d93b9 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/DiskFileItem.java @@ -0,0 +1,702 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.disk; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.UnsupportedEncodingException; +import java.util.Map; +import com.fr.third.org.apache.commons.io.IOUtils; +import com.fr.third.org.apache.commons.io.FileCleaner; +import com.fr.third.org.apache.commons.io.output.DeferredFileOutputStream; + +import com.fr.third.org.apache.commons.fileupload.FileItem; +import com.fr.third.org.apache.commons.fileupload.FileUploadException; +import com.fr.third.org.apache.commons.fileupload.ParameterParser; + + +/** + *

The default implementation of the + * {@link com.fr.third.org.apache.commons.fileupload.FileItem FileItem} interface. + * + *

After retrieving an instance of this class from a {@link + * com.fr.third.org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see + * {@link com.fr.third.org.apache.commons.fileupload.DiskFileUpload + * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may + * either request all contents of file at once using {@link #get()} or + * request an {@link java.io.InputStream InputStream} with + * {@link #getInputStream()} and process the file without attempting to load + * it into memory, which may come handy with large files. + * + *

When using the DiskFileItemFactory, then you should + * consider the following: Temporary files are automatically deleted as + * soon as they are no longer needed. (More precisely, when the + * corresponding instance of {@link java.io.File} is garbage collected.) + * This is done by the so-called reaper thread, which is started + * automatically when the class {@link FileCleaner} is loaded. + * It might make sense to terminate that thread, for example, if + * your web application ends. See the section on "Resource cleanup" + * in the users guide of commons-fileupload.

+ * + * @author Rafal Krzewski + * @author Sean Legassick + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class DiskFileItem + implements FileItem { + + // ----------------------------------------------------- Manifest constants + + + /** + * Default content charset to be used when no explicit charset + * parameter is provided by the sender. Media subtypes of the + * "text" type are defined to have a default charset value of + * "ISO-8859-1" when received via HTTP. + */ + public static final String DEFAULT_CHARSET = "ISO-8859-1"; + + + // ----------------------------------------------------------- Data members + + + /** + * UID used in unique file name generation. + */ + private static final String UID = + new java.rmi.server.UID().toString() + .replace(':', '_').replace('-', '_'); + + /** + * Counter used in unique identifier generation. + */ + private static int counter = 0; + + + /** + * The name of the form field as provided by the browser. + */ + private String fieldName; + + + /** + * The content type passed by the browser, or null if + * not defined. + */ + private String contentType; + + + /** + * Whether or not this item is a simple form field. + */ + private boolean isFormField; + + + /** + * The original filename in the user's filesystem. + */ + private String fileName; + + + /** + * The size of the item, in bytes. This is used to cache the size when a + * file item is moved from its original location. + */ + private long size = -1; + + + /** + * The threshold above which uploads will be stored on disk. + */ + private int sizeThreshold; + + + /** + * The directory in which uploaded files will be stored, if stored on disk. + */ + private File repository; + + + /** + * Cached contents of the file. + */ + private byte[] cachedContent; + + + /** + * Output stream for this item. + */ + private transient DeferredFileOutputStream dfos; + + /** + * File to allow for serialization of the content of this item. + */ + private File dfosFile; + + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs a new DiskFileItem instance. + * + * @param fieldName The name of the form field. + * @param contentType The content type passed by the browser or + * null if not specified. + * @param isFormField Whether or not this item is a plain form field, as + * opposed to a file upload. + * @param fileName The original filename in the user's filesystem, or + * null if not specified. + * @param sizeThreshold The threshold, in bytes, below which items will be + * retained in memory and above which they will be + * stored as a file. + * @param repository The data repository, which is the directory in + * which files will be created, should the item size + * exceed the threshold. + */ + public DiskFileItem(String fieldName, String contentType, + boolean isFormField, String fileName, int sizeThreshold, + File repository) { + this.fieldName = fieldName; + this.contentType = contentType; + this.isFormField = isFormField; + this.fileName = fileName; + this.sizeThreshold = sizeThreshold; + this.repository = repository; + } + + + // ------------------------------- Methods from javax.activation.DataSource + + + /** + * Returns an {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @return An {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @throws IOException if an error occurs. + */ + public InputStream getInputStream() + throws IOException { + if (!isInMemory()) { + return new FileInputStream(dfos.getFile()); + } + + if (cachedContent == null) { + cachedContent = dfos.getData(); + } + return new ByteArrayInputStream(cachedContent); + } + + + /** + * Returns the content type passed by the agent or null if + * not defined. + * + * @return The content type passed by the agent or null if + * not defined. + */ + public String getContentType() { + return contentType; + } + + + /** + * Returns the content charset passed by the agent or null if + * not defined. + * + * @return The content charset passed by the agent or null if + * not defined. + */ + public String getCharSet() { + ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + Map params = parser.parse(getContentType(), ';'); + return (String) params.get("charset"); + } + + + /** + * Returns the original filename in the client's filesystem. + * + * @return The original filename in the client's filesystem. + */ + public String getName() { + return fileName; + } + + + // ------------------------------------------------------- FileItem methods + + + /** + * Provides a hint as to whether or not the file contents will be read + * from memory. + * + * @return true if the file contents will be read + * from memory; false otherwise. + */ + public boolean isInMemory() { + if (cachedContent != null) { + return true; + } + return dfos.isInMemory(); + } + + + /** + * Returns the size of the file. + * + * @return The size of the file, in bytes. + */ + public long getSize() { + if (size >= 0) { + return size; + } else if (cachedContent != null) { + return cachedContent.length; + } else if (dfos.isInMemory()) { + return dfos.getData().length; + } else { + return dfos.getFile().length(); + } + } + + + /** + * Returns the contents of the file as an array of bytes. If the + * contents of the file were not yet cached in memory, they will be + * loaded from the disk storage and cached. + * + * @return The contents of the file as an array of bytes. + */ + public byte[] get() { + if (isInMemory()) { + if (cachedContent == null) { + cachedContent = dfos.getData(); + } + return cachedContent; + } + + byte[] fileData = new byte[(int) getSize()]; + FileInputStream fis = null; + + try { + fis = new FileInputStream(dfos.getFile()); + fis.read(fileData); + } catch (IOException e) { + fileData = null; + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + // ignore + } + } + } + + return fileData; + } + + + /** + * Returns the contents of the file as a String, using the specified + * encoding. This method uses {@link #get()} to retrieve the + * contents of the file. + * + * @param charset The charset to use. + * + * @return The contents of the file, as a string. + * + * @throws UnsupportedEncodingException if the requested character + * encoding is not available. + */ + public String getString(final String charset) + throws UnsupportedEncodingException { + return new String(get(), charset); + } + + + /** + * Returns the contents of the file as a String, using the default + * character encoding. This method uses {@link #get()} to retrieve the + * contents of the file. + * + * @return The contents of the file, as a string. + * + * @todo Consider making this method throw UnsupportedEncodingException. + */ + public String getString() { + byte[] rawdata = get(); + String charset = getCharSet(); + if (charset == null) { + charset = DEFAULT_CHARSET; + } + try { + return new String(rawdata, charset); + } catch (UnsupportedEncodingException e) { + return new String(rawdata); + } + } + + + /** + * A convenience method to write an uploaded item to disk. The client code + * is not concerned with whether or not the item is stored in memory, or on + * disk in a temporary location. They just want to write the uploaded item + * to a file. + *

+ * This implementation first attempts to rename the uploaded item to the + * specified destination file, if the item was originally written to disk. + * Otherwise, the data will be copied to the specified file. + *

+ * This method is only guaranteed to work once, the first time it + * is invoked for a particular item. This is because, in the event that the + * method renames a temporary file, that file will no longer be available + * to copy or rename again at a later time. + * + * @param file The File into which the uploaded item should + * be stored. + * + * @throws Exception if an error occurs. + */ + public void write(File file) throws Exception { + if (isInMemory()) { + FileOutputStream fout = null; + try { + fout = new FileOutputStream(file); + fout.write(get()); + } finally { + if (fout != null) { + fout.close(); + } + } + } else { + File outputFile = getStoreLocation(); + if (outputFile != null) { + // Save the length of the file + size = outputFile.length(); + /* + * The uploaded file is being stored on disk + * in a temporary location so move it to the + * desired file. + */ + if (!outputFile.renameTo(file)) { + BufferedInputStream in = null; + BufferedOutputStream out = null; + try { + in = new BufferedInputStream( + new FileInputStream(outputFile)); + out = new BufferedOutputStream( + new FileOutputStream(file)); + IOUtils.copy(in, out); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + // ignore + } + } + if (out != null) { + try { + out.close(); + } catch (IOException e) { + // ignore + } + } + } + } + } else { + /* + * For whatever reason we cannot write the + * file to disk. + */ + throw new FileUploadException( + "Cannot write uploaded file to disk!"); + } + } + } + + + /** + * Deletes the underlying storage for a file item, including deleting any + * associated temporary disk file. Although this storage will be deleted + * automatically when the FileItem instance is garbage + * collected, this method can be used to ensure that this is done at an + * earlier time, thus preserving system resources. + */ + public void delete() { + cachedContent = null; + File outputFile = getStoreLocation(); + if (outputFile != null && outputFile.exists()) { + outputFile.delete(); + } + } + + + /** + * Returns the name of the field in the multipart form corresponding to + * this file item. + * + * @return The name of the form field. + * + * @see #setFieldName(java.lang.String) + * + */ + public String getFieldName() { + return fieldName; + } + + + /** + * Sets the field name used to reference this file item. + * + * @param fieldName The name of the form field. + * + * @see #getFieldName() + * + */ + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + + /** + * Determines whether or not a FileItem instance represents + * a simple form field. + * + * @return true if the instance represents a simple form + * field; false if it represents an uploaded file. + * + * @see #setFormField(boolean) + * + */ + public boolean isFormField() { + return isFormField; + } + + + /** + * Specifies whether or not a FileItem instance represents + * a simple form field. + * + * @param state true if the instance represents a simple form + * field; false if it represents an uploaded file. + * + * @see #isFormField() + * + */ + public void setFormField(boolean state) { + isFormField = state; + } + + + /** + * Returns an {@link java.io.OutputStream OutputStream} that can + * be used for storing the contents of the file. + * + * @return An {@link java.io.OutputStream OutputStream} that can be used + * for storing the contensts of the file. + * + * @throws IOException if an error occurs. + */ + public OutputStream getOutputStream() + throws IOException { + if (dfos == null) { + File outputFile = getTempFile(); + dfos = new DeferredFileOutputStream(sizeThreshold, outputFile); + } + return dfos; + } + + + // --------------------------------------------------------- Public methods + + + /** + * Returns the {@link java.io.File} object for the FileItem's + * data's temporary location on the disk. Note that for + * FileItems that have their data stored in memory, + * this method will return null. When handling large + * files, you can use {@link java.io.File#renameTo(java.io.File)} to + * move the file to new location without copying the data, if the + * source and destination locations reside within the same logical + * volume. + * + * @return The data file, or null if the data is stored in + * memory. + */ + public File getStoreLocation() { + return dfos.getFile(); + } + + + // ------------------------------------------------------ Protected methods + + + /** + * Removes the file contents from the temporary storage. + */ + protected void finalize() { + File outputFile = dfos.getFile(); + + if (outputFile != null && outputFile.exists()) { + outputFile.delete(); + } + } + + + /** + * Creates and returns a {@link java.io.File File} representing a uniquely + * named temporary file in the configured repository path. The lifetime of + * the file is tied to the lifetime of the FileItem instance; + * the file will be deleted when the instance is garbage collected. + * + * @return The {@link java.io.File File} to be used for temporary storage. + */ + protected File getTempFile() { + File tempDir = repository; + if (tempDir == null) { + tempDir = new File(System.getProperty("java.io.tmpdir")); + } + + String tempFileName = "upload_" + UID + "_" + getUniqueId() + ".tmp"; + + File f = new File(tempDir, tempFileName); + FileCleaner.track(f, this); + return f; + } + + + // -------------------------------------------------------- Private methods + + + /** + * Returns an identifier that is unique within the class loader used to + * load this class, but does not have random-like apearance. + * + * @return A String with the non-random looking instance identifier. + */ + private static String getUniqueId() { + final int limit = 100000000; + int current; + synchronized (DiskFileItem.class) { + current = counter++; + } + String id = Integer.toString(current); + + // If you manage to get more than 100 million of ids, you'll + // start getting ids longer than 8 characters. + if (current < limit) { + id = ("00000000" + id).substring(id.length()); + } + return id; + } + + + + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object. + */ + public String toString() { + return "name=" + this.getName() + + ", StoreLocation=" + + String.valueOf(this.getStoreLocation()) + + ", size=" + + this.getSize() + + "bytes, " + + "isFormField=" + isFormField() + + ", FieldName=" + + this.getFieldName(); + } + + + // -------------------------------------------------- Serialization methods + + + /** + * Writes the state of this object during serialization. + * + * @param out The stream to which the state should be written. + * + * @throws IOException if an error occurs. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + // Read the data + if (dfos.isInMemory()) { + cachedContent = get(); + } else { + cachedContent = null; + dfosFile = dfos.getFile(); + } + + // write out values + out.defaultWriteObject(); + } + + /** + * Reads the state of this object during deserialization. + * + * @param in The stream from which the state should be read. + * + * @throws IOException if an error occurs. + * @throws ClassNotFoundException if class cannot be found. + */ + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + // read values + in.defaultReadObject(); + + OutputStream output = getOutputStream(); + if (cachedContent != null) { + output.write(cachedContent); + } else { + FileInputStream input = new FileInputStream(dfosFile); + + IOUtils.copy(input, output); + dfosFile.delete(); + dfosFile = null; + } + output.close(); + + cachedContent = null; + } + +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/DiskFileItemFactory.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/DiskFileItemFactory.java new file mode 100755 index 000000000..34c852451 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/DiskFileItemFactory.java @@ -0,0 +1,195 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.disk; + +import java.io.File; + +import com.fr.third.org.apache.commons.fileupload.FileItem; +import com.fr.third.org.apache.commons.fileupload.FileItemFactory; + +/** + *

The default {@link com.fr.third.org.apache.commons.fileupload.FileItemFactory} + * implementation. This implementation creates + * {@link com.fr.third.org.apache.commons.fileupload.FileItem} instances which keep their + * content either in memory, for smaller items, or in a temporary file on disk, + * for larger items. The size threshold, above which content will be stored on + * disk, is configurable, as is the directory in which temporary files will be + * created.

+ * + *

If not otherwise configured, the default configuration values are as + * follows: + *

+ *

+ * + *

When using the DiskFileItemFactory, then you should + * consider the following: Temporary files are automatically deleted as + * soon as they are no longer needed. (More precisely, when the + * corresponding instance of {@link java.io.File} is garbage collected.) + * This is done by the so-called reaper thread, which is started + * automatically when the class {@link org.apache.commons.io.FileCleaner} + * is loaded. It might make sense to terminate that thread, for example, + * if your web application ends. See the section on "Resource cleanup" + * in the users guide of commons-fileupload.

+ * + * @author Martin Cooper + * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class DiskFileItemFactory implements FileItemFactory { + + // ----------------------------------------------------- Manifest constants + + + /** + * The default threshold above which uploads will be stored on disk. + */ + public static final int DEFAULT_SIZE_THRESHOLD = 10240; + + + // ----------------------------------------------------- Instance Variables + + + /** + * The directory in which uploaded files will be stored, if stored on disk. + */ + private File repository; + + + /** + * The threshold above which uploads will be stored on disk. + */ + private int sizeThreshold = DEFAULT_SIZE_THRESHOLD; + + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs an unconfigured instance of this class. The resulting factory + * may be configured by calling the appropriate setter methods. + */ + public DiskFileItemFactory() { + // Does nothing. + } + + + /** + * Constructs a preconfigured instance of this class. + * + * @param sizeThreshold The threshold, in bytes, below which items will be + * retained in memory and above which they will be + * stored as a file. + * @param repository The data repository, which is the directory in + * which files will be created, should the item size + * exceed the threshold. + */ + public DiskFileItemFactory(int sizeThreshold, File repository) { + this.sizeThreshold = sizeThreshold; + this.repository = repository; + } + + + // ------------------------------------------------------------- Properties + + + /** + * Returns the directory used to temporarily store files that are larger + * than the configured size threshold. + * + * @return The directory in which temporary files will be located. + * + * @see #setRepository(java.io.File) + * + */ + public File getRepository() { + return repository; + } + + + /** + * Sets the directory used to temporarily store files that are larger + * than the configured size threshold. + * + * @param repository The directory in which temporary files will be located. + * + * @see #getRepository() + * + */ + public void setRepository(File repository) { + this.repository = repository; + } + + + /** + * Returns the size threshold beyond which files are written directly to + * disk. The default value is 10240 bytes. + * + * @return The size threshold, in bytes. + * + * @see #setSizeThreshold(int) + */ + public int getSizeThreshold() { + return sizeThreshold; + } + + + /** + * Sets the size threshold beyond which files are written directly to disk. + * + * @param sizeThreshold The size threshold, in bytes. + * + * @see #getSizeThreshold() + * + */ + public void setSizeThreshold(int sizeThreshold) { + this.sizeThreshold = sizeThreshold; + } + + + // --------------------------------------------------------- Public Methods + + /** + * Create a new {@link com.fr.third.org.apache.commons.fileupload.disk.DiskFileItem} + * instance from the supplied parameters and the local factory + * configuration. + * + * @param fieldName The name of the form field. + * @param contentType The content type of the form field. + * @param isFormField true if this is a plain form field; + * false otherwise. + * @param fileName The name of the uploaded file, if any, as supplied + * by the browser or other client. + * + * @return The newly created file item. + */ + public FileItem createItem( + String fieldName, + String contentType, + boolean isFormField, + String fileName + ) { + return new DiskFileItem(fieldName, contentType, + isFormField, fileName, sizeThreshold, repository); + } + +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/package.html b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/package.html new file mode 100755 index 000000000..6f3ec1b7f --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/disk/package.html @@ -0,0 +1,58 @@ + + + + + Overview of the com.fr.third.org.apache.commons.fileupload.disk component + + +

+ A disk-based implementation of the + {@link com.fr.third.org.apache.commons.fileupload.FileItem FileItem} + interface. This implementation retains smaller items in memory, while + writing larger ones to disk. The threshold between these two is + configurable, as is the location of files that are written to disk. +

+

+ In typical usage, an instance of + {@link com.fr.third.org.apache.commons.fileupload.disk.DiskFileItemFactory DiskFileItemFactory} + would be created, configured, and then passed to a + {@link com.fr.third.org.apache.commons.fileupload.FileUpload FileUpload} + implementation such as + {@link com.fr.third.org.apache.commons.fileupload.servlet.ServletFileUpload ServletFileUpload} + or + {@link com.fr.third.org.apache.commons.fileupload.portlet.PortletFileUpload PortletFileUpload}. +

+

+ The following code fragment demonstrates this usage. +

+
+        DiskFileItemFactory factory = new DiskFileItemFactory();
+        // maximum size that will be stored in memory
+        factory.setSizeThreshold(4096);
+        // the location for saving data that is larger than getSizeThreshold()
+        factory.setRepository(new File("/tmp"));
+
+        ServletFileUpload upload = new ServletFileUpload(factory);
+
+

+ Please see the FileUpload + User Guide + for further details and examples of how to use this package. +

+ + diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/package.html b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/package.html new file mode 100755 index 000000000..e3206501e --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/package.html @@ -0,0 +1,90 @@ + + + + + Overview of the com.fr.third.org.apache.commons.fileupload component + + +

+ A component for handling HTML file uploads as specified by + RFC 1867. + This component provides support for uploads within both servlets (JSR 53) + and portlets (JSR 168). +

+

+ While this package provides the generic functionality for file uploads, + these classes are not typically used directly. Instead, normal usage + involves one of the provided extensions of + {@link com.fr.third.org.apache.commons.fileupload.FileUpload FileUpload} such as + {@link com.fr.third.org.apache.commons.fileupload.servlet.ServletFileUpload ServletFileUpload} + or + {@link com.fr.third.org.apache.commons.fileupload.portlet.PortletFileUpload PortletFileUpload}, + together with a factory for + {@link com.fr.third.org.apache.commons.fileupload.FileItem FileItem} instances, + such as + {@link com.fr.third.org.apache.commons.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}. +

+

+ The following is a brief example of typical usage in a servlet, storing + the uploaded files on disk. +

+
+    public void doPost(HttpServletRequest req, HttpServletResponse res) {
+        DiskFileItemFactory factory = new DiskFileItemFactory();
+        // maximum size that will be stored in memory
+        factory.setSizeThreshold(4096);
+        // the location for saving data that is larger than getSizeThreshold()
+        factory.setRepository(new File("/tmp"));
+
+        ServletFileUpload upload = new ServletFileUpload(factory);
+        // maximum size before a FileUploadException will be thrown
+        upload.setSizeMax(1000000);
+
+        List fileItems = upload.parseRequest(req);
+        // assume we know there are two files. The first file is a small
+        // text file, the second is unknown and is written to a file on
+        // the server
+        Iterator i = fileItems.iterator();
+        String comment = ((FileItem)i.next()).getString();
+        FileItem fi = (FileItem)i.next();
+        // filename on the client
+        String fileName = fi.getName();
+        // save comment and filename to database
+        ...
+        // write the file
+        fi.write(new File("/www/uploads/", fileName));
+    }
+
+

+ In the example above, the first file is loaded into memory as a + String. Before calling the getString method, + the data may have been in memory or on disk depending on its size. The + second file we assume it will be large and therefore never explicitly + load it into memory, though if it is less than 4096 bytes it will be + in memory before it is written to its final location. When writing to + the final location, if the data is larger than the threshold, an attempt + is made to rename the temporary file to the given location. If it cannot + be renamed, it is streamed to the new location. +

+

+ Please see the FileUpload + User Guide + for further details and examples of how to use this package. +

+ + diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/PortletFileUpload.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/PortletFileUpload.java new file mode 100755 index 000000000..d8cddda00 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/PortletFileUpload.java @@ -0,0 +1,142 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.portlet; + +import java.io.IOException; +import java.util.List; + +import javax.portlet.ActionRequest; + +import com.fr.third.org.apache.commons.fileupload.FileItemFactory; +import com.fr.third.org.apache.commons.fileupload.FileItemIterator; +import com.fr.third.org.apache.commons.fileupload.FileUpload; +import com.fr.third.org.apache.commons.fileupload.FileUploadBase; +import com.fr.third.org.apache.commons.fileupload.FileUploadException; + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(javax.servlet.http.HttpServletRequest)} to acquire a list + * of {@link com.fr.third.org.apache.commons.fileupload.FileItem FileItems} associated + * with a given HTML widget.

+ * + *

How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.

+ * + * @author Rafal Krzewski + * @author Daniel Rall + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class PortletFileUpload extends FileUpload { + + // ---------------------------------------------------------- Class methods + + + /** + * Utility method that determines whether the request contains multipart + * content. + * + * @param request The portlet request to be evaluated. Must be non-null. + * + * @return true if the request is multipart; + * false otherwise. + */ + public static final boolean isMultipartContent(ActionRequest request) { + return FileUploadBase.isMultipartContent( + new PortletRequestContext(request)); + } + + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs an uninitialised instance of this class. A factory must be + * configured, using setFileItemFactory(), before attempting + * to parse requests. + * + * @see FileUpload#FileUpload(FileItemFactory) + */ + public PortletFileUpload() { + super(); + } + + + /** + * Constructs an instance of this class which uses the supplied factory to + * create FileItem instances. + * + * @see FileUpload#FileUpload() + * @param fileItemFactory The factory to use for creating file items. + */ + public PortletFileUpload(FileItemFactory fileItemFactory) { + super(fileItemFactory); + } + + + // --------------------------------------------------------- Public methods + + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The portlet request to be parsed. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + public List /* FileItem */ parseRequest(ActionRequest request) + throws FileUploadException { + return parseRequest(new PortletRequestContext(request)); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The portlet request to be parsed. + * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * @throws IOException An I/O error occurred. This may be a network + * error while communicating with the client or a problem while + * storing the uploaded content. + */ + public FileItemIterator getItemIterator(ActionRequest request) + throws FileUploadException, IOException { + return super.getItemIterator(new PortletRequestContext(request)); + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/PortletRequestContext.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/PortletRequestContext.java new file mode 100755 index 000000000..d4cfd0a4e --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/PortletRequestContext.java @@ -0,0 +1,108 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.portlet; + +import java.io.InputStream; +import java.io.IOException; +import javax.portlet.ActionRequest; +import com.fr.third.org.apache.commons.fileupload.RequestContext; + +/** + *

Provides access to the request information needed for a request made to + * a portlet.

+ * + * @author Martin Cooper + * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class PortletRequestContext implements RequestContext { + + // ----------------------------------------------------- Instance Variables + + /** + * The request for which the context is being provided. + */ + private ActionRequest request; + + + // ----------------------------------------------------------- Constructors + + /** + * Construct a context for this request. + * + * @param request The request to which this context applies. + */ + public PortletRequestContext(ActionRequest request) { + this.request = request; + } + + + // --------------------------------------------------------- Public Methods + + /** + * Retrieve the character encoding for the request. + * + * @return The character encoding for the request. + */ + public String getCharacterEncoding() { + return request.getCharacterEncoding(); + } + + /** + * Retrieve the content type of the request. + * + * @return The content type of the request. + */ + public String getContentType() { + return request.getContentType(); + } + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + */ + public int getContentLength() { + return request.getContentLength(); + } + + /** + * Retrieve the input stream for the request. + * + * @return The input stream for the request. + * + * @throws IOException if a problem occurs. + */ + public InputStream getInputStream() throws IOException { + return request.getPortletInputStream(); + } + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object. + */ + public String toString() { + return "ContentLength=" + + this.getContentLength() + + ", ContentType=" + + this.getContentType(); + } + +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/package.html b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/package.html new file mode 100755 index 000000000..887ac3187 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/portlet/package.html @@ -0,0 +1,49 @@ + + + + + Overview of the com.fr.third.org.apache.commons.fileupload.portlet component + + +

+ An implementation of + {@link com.fr.third.org.apache.commons.fileupload.FileUpload FileUpload} + for use in portlets conforming to JSR 168. This implementation requires + only access to the portlet's current ActionRequest instance, + and a suitable + {@link com.fr.third.org.apache.commons.fileupload.FileItemFactory FileItemFactory} + implementation, such as + {@link com.fr.third.org.apache.commons.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}. +

+

+ The following code fragment demonstrates typical usage. +

+
+        DiskFileItemFactory factory = new DiskFileItemFactory();
+        // Configure the factory here, if desired.
+        PortletFileUpload upload = new PortletFileUpload(factory);
+        // Configure the uploader here, if desired.
+        List fileItems = upload.parseRequest(request);
+
+

+ Please see the FileUpload + User Guide + for further details and examples of how to use this package. +

+ + diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/FileCleanerCleanup.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/FileCleanerCleanup.java new file mode 100755 index 000000000..c1270a181 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/FileCleanerCleanup.java @@ -0,0 +1,49 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.servlet; + +import javax.servlet.ServletContextListener; +import javax.servlet.ServletContextEvent; + +import com.fr.third.org.apache.commons.io.FileCleaner; + + +/** + * A servlet context listener, which ensures that the + * {@link org.apache.commons.io.FileCleaner FileCleaner's} + * reaper thread is terminated, + * when the web application is destroyed. + */ +public class FileCleanerCleanup implements ServletContextListener { + /** + * Called when the web application is initialized. Does + * nothing. + * @param sce The servlet context (ignored). + */ + public void contextInitialized(ServletContextEvent sce) { + // Does nothing. + } + + /** + * Called when the web application is being destroyed. + * Calls {@link FileCleaner#exitWhenFinished()}. + * @param sce The servlet context (ignored). + */ + public void contextDestroyed(ServletContextEvent sce) { + FileCleaner.exitWhenFinished(); + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/ServletFileUpload.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/ServletFileUpload.java new file mode 100755 index 000000000..44e0613b4 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/ServletFileUpload.java @@ -0,0 +1,150 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.servlet; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import com.fr.third.org.apache.commons.fileupload.FileItemFactory; +import com.fr.third.org.apache.commons.fileupload.FileItemIterator; +import com.fr.third.org.apache.commons.fileupload.FileUpload; +import com.fr.third.org.apache.commons.fileupload.FileUploadException; + +/** + *

High level API for processing file uploads.

+ * + *

This class handles multiple files per single HTML widget, sent using + * multipart/mixed encoding type, as specified by + * RFC 1867. Use {@link + * #parseRequest(HttpServletRequest)} to acquire a list of {@link + * com.fr.third.org.apache.commons.fileupload.FileItem}s associated with a given HTML + * widget.

+ * + *

How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.

+ * + * @author Rafal Krzewski + * @author Daniel Rall + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id$ + */ +public class ServletFileUpload extends FileUpload { + + // ---------------------------------------------------------- Class methods + + + /** + * Utility method that determines whether the request contains multipart + * content. + * + * @param request The servlet request to be evaluated. Must be non-null. + * + * @return true if the request is multipart; + * false otherwise. + */ + public static final boolean isMultipartContent( + HttpServletRequest request) { + if (!"post".equals(request.getMethod().toLowerCase())) { + return false; + } + String contentType = request.getContentType(); + if (contentType == null) { + return false; + } + if (contentType.toLowerCase().startsWith(MULTIPART)) { + return true; + } + return false; + } + + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs an uninitialised instance of this class. A factory must be + * configured, using setFileItemFactory(), before attempting + * to parse requests. + * + * @see FileUpload#FileUpload(FileItemFactory) + */ + public ServletFileUpload() { + super(); + } + + + /** + * Constructs an instance of this class which uses the supplied factory to + * create FileItem instances. + * + * @see FileUpload#FileUpload() + * @param fileItemFactory The factory to use for creating file items. + */ + public ServletFileUpload(FileItemFactory fileItemFactory) { + super(fileItemFactory); + } + + + // --------------------------------------------------------- Public methods + + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The servlet request to be parsed. + * + * @return A list of FileItem instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + public List /* FileItem */ parseRequest(HttpServletRequest request) + throws FileUploadException { + return parseRequest(new ServletRequestContext(request)); + } + + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The servlet request to be parsed. + * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * @throws IOException An I/O error occurred. This may be a network + * error while communicating with the client or a problem while + * storing the uploaded content. + */ + public FileItemIterator getItemIterator(HttpServletRequest request) + throws FileUploadException, IOException { + return super.getItemIterator(new ServletRequestContext(request)); + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/ServletRequestContext.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/ServletRequestContext.java new file mode 100755 index 000000000..371049994 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/ServletRequestContext.java @@ -0,0 +1,107 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.servlet; + +import java.io.InputStream; +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import com.fr.third.org.apache.commons.fileupload.RequestContext; + +/** + *

Provides access to the request information needed for a request made to + * an HTTP servlet.

+ * + * @author Martin Cooper + * + * @since FileUpload 1.1 + * + * @version $Id$ + */ +public class ServletRequestContext implements RequestContext { + + // ----------------------------------------------------- Instance Variables + + /** + * The request for which the context is being provided. + */ + private HttpServletRequest request; + + + // ----------------------------------------------------------- Constructors + + /** + * Construct a context for this request. + * + * @param request The request to which this context applies. + */ + public ServletRequestContext(HttpServletRequest request) { + this.request = request; + } + + + // --------------------------------------------------------- Public Methods + + /** + * Retrieve the character encoding for the request. + * + * @return The character encoding for the request. + */ + public String getCharacterEncoding() { + return request.getCharacterEncoding(); + } + + /** + * Retrieve the content type of the request. + * + * @return The content type of the request. + */ + public String getContentType() { + return request.getContentType(); + } + + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + */ + public int getContentLength() { + return request.getContentLength(); + } + + /** + * Retrieve the input stream for the request. + * + * @return The input stream for the request. + * + * @throws IOException if a problem occurs. + */ + public InputStream getInputStream() throws IOException { + return request.getInputStream(); + } + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object. + */ + public String toString() { + return "ContentLength=" + + this.getContentLength() + + ", ContentType=" + + this.getContentType(); + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/package.html b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/package.html new file mode 100755 index 000000000..7efde0508 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/servlet/package.html @@ -0,0 +1,49 @@ + + + + + Overview of the com.fr.third.org.apache.commons.fileupload.servlet component + + +

+ An implementation of + {@link com.fr.third.org.apache.commons.fileupload.FileUpload FileUpload} + for use in servlets conforming to JSR 53. This implementation requires + only access to the servlet's current HttpServletRequest + instance, and a suitable + {@link com.fr.third.org.apache.commons.fileupload.FileItemFactory FileItemFactory} + implementation, such as + {@link com.fr.third.org.apache.commons.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}. +

+

+ The following code fragment demonstrates typical usage. +

+
+        DiskFileItemFactory factory = new DiskFileItemFactory();
+        // Configure the factory here, if desired.
+        ServletFileUpload upload = new ServletFileUpload(factory);
+        // Configure the uploader here, if desired.
+        List fileItems = upload.parseRequest(request);
+
+

+ Please see the FileUpload + User Guide + for further details and examples of how to use this package. +

+ + diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/Closeable.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/Closeable.java new file mode 100755 index 000000000..1b1bb86bf --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/Closeable.java @@ -0,0 +1,38 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.util; + +import java.io.IOException; + + +/** + * Interface of an object, which may be closed. + */ +public interface Closeable { + /** + * Closes the object. + * @throws IOException An I/O error occurred. + */ + void close() throws IOException; + + /** + * Returns, whether the object is already closed. + * @return True, if the object is closed, otherwise false. + * @throws IOException An I/O error occurred. + */ + boolean isClosed() throws IOException; +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/LimitedInputStream.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/LimitedInputStream.java new file mode 100755 index 000000000..389b432a6 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/LimitedInputStream.java @@ -0,0 +1,155 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.util; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + + +/** + * An input stream, which limits its data size. This stream is + * used, if the content length is unknown. + */ +public abstract class LimitedInputStream + extends FilterInputStream implements Closeable { + /** + * The maximum size of an item, in bytes. + */ + private long sizeMax; + /** + * The current number of bytes. + */ + private long count; + /** + * Whether this stream is already closed. + */ + private boolean closed; + + /** + * Creates a new instance. + * @param pIn The input stream, which shall be limited. + * @param pSizeMax The limit; no more than this number of bytes + * shall be returned by the source stream. + */ + public LimitedInputStream(InputStream pIn, long pSizeMax) { + super(pIn); + sizeMax = pSizeMax; + } + + /** + * Called to indicate, that the input streams limit has + * been exceeded. + * @param pSizeMax The input streams limit, in bytes. + * @param pCount The actual number of bytes. + * @throws IOException The called method is expected + * to raise an IOException. + */ + protected abstract void raiseError(long pSizeMax, long pCount) + throws IOException; + + /** Called to check, whether the input streams + * limit is reached. + * @throws IOException The given limit is exceeded. + */ + private void checkLimit() throws IOException { + if (count > sizeMax) { + raiseError(sizeMax, count); + } + } + + /** + * Reads the next byte of data from this input stream. The value + * byte is returned as an int in the range + * 0 to 255. If no byte is available + * because the end of the stream has been reached, the value + * -1 is returned. This method blocks until input data + * is available, the end of the stream is detected, or an exception + * is thrown. + *

+ * This method + * simply performs in.read() and returns the result. + * + * @return the next byte of data, or -1 if the end of the + * stream is reached. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + public int read() throws IOException { + int res = super.read(); + if (res != -1) { + count++; + checkLimit(); + } + return res; + } + + /** + * Reads up to len bytes of data from this input stream + * into an array of bytes. If len is not zero, the method + * blocks until some input is available; otherwise, no + * bytes are read and 0 is returned. + *

+ * This method simply performs in.read(b, off, len) + * and returns the result. + * + * @param b the buffer into which the data is read. + * @param off The start offset in the destination array + * b. + * @param len the maximum number of bytes read. + * @return the total number of bytes read into the buffer, or + * -1 if there is no more data because the end of + * the stream has been reached. + * @exception NullPointerException If b is null. + * @exception IndexOutOfBoundsException If off is negative, + * len is negative, or len is greater than + * b.length - off + * @exception IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + public int read(byte[] b, int off, int len) throws IOException { + int res = super.read(b, off, len); + if (res > 0) { + count += res; + checkLimit(); + } + return res; + } + + /** + * Returns, whether this stream is already closed. + * @return True, if the stream is closed, otherwise false. + * @throws IOException An I/O error occurred. + */ + public boolean isClosed() throws IOException { + return closed; + } + + /** + * Closes this input stream and releases any system resources + * associated with the stream. + * This + * method simply performs in.close(). + * + * @exception IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + public void close() throws IOException { + closed = true; + super.close(); + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/Streams.java b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/Streams.java new file mode 100755 index 000000000..cde4ee7f2 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/Streams.java @@ -0,0 +1,166 @@ +/* + * 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. + */ +package com.fr.third.org.apache.commons.fileupload.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/** Utility class for working with streams. + */ +public final class Streams { + /** + * Private constructor, to prevent instantiation. + * This class has only static methods. + */ + private Streams() { + // Does nothing + } + + /** + * Default buffer size for use in + * {@link #copy(InputStream, OutputStream, boolean)}. + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * Copies the contents of the given {@link InputStream} + * to the given {@link OutputStream}. Shortcut for + *

+     *   copy(pInputStream, pOutputStream, new byte[8192]);
+     * 
+ * @param pInputStream The input stream, which is being read. + * It is guaranteed, that {@link InputStream#close()} is called + * on the stream. + * @param pOutputStream The output stream, to which data should + * be written. May be null, in which case the input streams + * contents are simply discarded. + * @param pClose True guarantees, that {@link OutputStream#close()} + * is called on the stream. False indicates, that only + * {@link OutputStream#flush()} should be called finally. + * + * @return Number of bytes, which have been copied. + * @throws IOException An I/O error occurred. + */ + public static long copy(InputStream pInputStream, + OutputStream pOutputStream, boolean pClose) + throws IOException { + return copy(pInputStream, pOutputStream, pClose, + new byte[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copies the contents of the given {@link InputStream} + * to the given {@link OutputStream}. + * @param pIn The input stream, which is being read. + * It is guaranteed, that {@link InputStream#close()} is called + * on the stream. + * @param pOut The output stream, to which data should + * be written. May be null, in which case the input streams + * contents are simply discarded. + * @param pClose True guarantees, that {@link OutputStream#close()} + * is called on the stream. False indicates, that only + * {@link OutputStream#flush()} should be called finally. + * @param pBuffer Temporary buffer, which is to be used for + * copying data. + * @return Number of bytes, which have been copied. + * @throws IOException An I/O error occurred. + */ + public static long copy(InputStream pIn, + OutputStream pOut, boolean pClose, + byte[] pBuffer) + throws IOException { + OutputStream out = pOut; + InputStream in = pIn; + try { + long total = 0; + for (;;) { + int res = in.read(pBuffer); + if (res == -1) { + break; + } + if (res > 0) { + total += res; + if (out != null) { + out.write(pBuffer, 0, res); + } + } + } + if (out != null) { + if (pClose) { + out.close(); + } else { + out.flush(); + } + out = null; + } + in.close(); + in = null; + return total; + } finally { + if (in != null) { + try { + in.close(); + } catch (Throwable t) { + /* Ignore me */ + } + } + if (pClose && out != null) { + try { + out.close(); + } catch (Throwable t) { + /* Ignore me */ + } + } + } + } + + /** + * This convenience method allows to read a + * {@link com.fr.third.org.apache.commons.fileupload.FileItemStream}'s + * content into a string. The platform's default character encoding + * is used for converting bytes into characters. + * @param pStream The input stream to read. + * @see #asString(InputStream, String) + * @return The streams contents, as a string. + * @throws IOException An I/O error occurred. + */ + public static String asString(InputStream pStream) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copy(pStream, baos, true); + return baos.toString(); + } + + /** + * This convenience method allows to read a + * {@link com.fr.third.org.apache.commons.fileupload.FileItemStream}'s + * content into a string, using the given character encoding. + * @param pStream The input stream to read. + * @param pEncoding The character encoding, typically "UTF-8". + * @see #asString(InputStream) + * @return The streams contents, as a string. + * @throws IOException An I/O error occurred. + */ + public static String asString(InputStream pStream, String pEncoding) + throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copy(pStream, baos, true); + return baos.toString(pEncoding); + } +} diff --git a/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/package.html b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/package.html new file mode 100755 index 000000000..acb098eb5 --- /dev/null +++ b/fine-commons-fileupload/src/com/fr/third/org/apache/commons/fileupload/util/package.html @@ -0,0 +1,29 @@ + + + + + Overview of the com.fr.third.org.apache.commons.fileupload.util component + + +

+ This package contains various IO related utility classes + or methods, which are basically reusable and not necessarily + restricted to the scope of a file upload. +

+ +