+++ /dev/null
-/*
- * 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 org.apache.tomcat.util.http.fileupload;
-
-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;
-}
+++ /dev/null
-/*
- * 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 org.apache.tomcat.util.http.fileupload;
-
-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.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.Map;
-
-
-/**
- * <p> The default implementation of the
- * {@link org.apache.commons.fileupload.FileItem FileItem} interface.
- *
- * <p> After retrieving an instance of this class from a {@link
- * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
- * {@link 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.
- *
- * <p>When using the <code>DiskFileItemFactory</code>, 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.</p>
- *
- * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
- * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
- * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
- * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
- * @author Sean C. Sullivan
- *
- * @since FileUpload 1.1
- *
- * @version $Id$
- */
-public class DiskFileItem
- implements FileItem, FileItemHeadersSupport {
-
- // ----------------------------------------------------- Manifest constants
-
- /**
- * The UID to use when serializing this instance.
- */
- private static final long serialVersionUID = 2237570099615271025L;
-
-
- /**
- * 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 <code>null</code> 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;
-
- /**
- * The temporary file to use.
- */
- private transient File tempFile;
-
- /**
- * File to allow for serialization of the content of this item.
- */
- private File dfosFile;
-
- /**
- * The file items headers.
- */
- private FileItemHeaders headers;
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Constructs a new <code>DiskFileItem</code> instance.
- *
- * @param fieldName The name of the form field.
- * @param contentType The content type passed by the browser or
- * <code>null</code> 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
- * <code>null</code> 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 <code>null</code> if
- * not defined.
- *
- * @return The content type passed by the agent or <code>null</code> if
- * not defined.
- */
- public String getContentType() {
- return contentType;
- }
-
-
- /**
- * Returns the content charset passed by the agent or <code>null</code> if
- * not defined.
- *
- * @return The content charset passed by the agent or <code>null</code> if
- * not defined.
- */
- public String getCharSet() {
- ParameterParser parser = new ParameterParser();
- parser.setLowerCaseNames(true);
- // Parameter parser can handle null input
- Map<String,String> params = parser.parse(getContentType(), ';');
- return 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 <code>true</code> if the file contents will be read
- * from memory; <code>false</code> 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.
- * <p>
- * 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.
- * <p>
- * This method is only guaranteed to work <em>once</em>, 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 <code>File</code> 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 <code>FileItem</code> 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 <code>FileItem</code> instance represents
- * a simple form field.
- *
- * @return <code>true</code> if the instance represents a simple form
- * field; <code>false</code> if it represents an uploaded file.
- *
- * @see #setFormField(boolean)
- *
- */
- public boolean isFormField() {
- return isFormField;
- }
-
-
- /**
- * Specifies whether or not a <code>FileItem</code> instance represents
- * a simple form field.
- *
- * @param state <code>true</code> if the instance represents a simple form
- * field; <code>false</code> 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 <code>FileItem</code>'s
- * data's temporary location on the disk. Note that for
- * <code>FileItem</code>s that have their data stored in memory,
- * this method will return <code>null</code>. 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 <code>null</code> if the data is stored in
- * memory.
- */
- public File getStoreLocation() {
- return dfos == null ? null : dfos.getFile();
- }
-
-
- // ------------------------------------------------------ Protected methods
-
-
- /**
- * Removes the file contents from the temporary storage.
- */
- @Override
- 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 <code>FileItem</code> 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() {
- if (tempFile == null) {
- File tempDir = repository;
- if (tempDir == null) {
- tempDir = new File(System.getProperty("java.io.tmpdir"));
- }
-
- String tempFileName =
- "upload_" + UID + "_" + getUniqueId() + ".tmp";
-
- tempFile = new File(tempDir, tempFileName);
- }
- return tempFile;
- }
-
-
- // -------------------------------------------------------- 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.
- */
- @Override
- 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;
- }
-
- /**
- * Returns the file item headers.
- * @return The file items headers.
- */
- public FileItemHeaders getHeaders() {
- return headers;
- }
-
- /**
- * Sets the file item headers.
- * @param pHeaders The file items headers.
- */
- public void setHeaders(FileItemHeaders pHeaders) {
- headers = pHeaders;
- }
-}
+++ /dev/null
-/*
- * 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 org.apache.tomcat.util.http.fileupload;
-
-import java.io.File;
-
-
-/**
- * <p>The default {@link org.apache.commons.fileupload.FileItemFactory}
- * implementation. This implementation creates
- * {@link 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.</p>
- *
- * <p>If not otherwise configured, the default configuration values are as
- * follows:
- * <ul>
- * <li>Size threshold is 10KB.</li>
- * <li>Repository is the system default temp directory, as returned by
- * <code>System.getProperty("java.io.tmpdir")</code>.</li>
- * </ul>
- * </p>
- *
- * <p>When using the <code>DiskFileItemFactory</code>, 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.)
- * Cleaning up those files is done by an instance of
- * {@link FileCleaningTracker}, and an associated thread. In a complex
- * environment, for example in a web application, you should consider
- * terminating this thread, for example, when your web application
- * ends. See the section on "Resource cleanup"
- * in the users guide of commons-fileupload.</p>
- *
- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
- *
- * @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;
-
-
- /**
- * <p>The instance of {@link FileCleaningTracker}, which is responsible
- * for deleting temporary files.</p>
- * <p>May be null, if tracking files is not required.</p>
- */
- private FileCleaningTracker fileCleaningTracker;
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Constructs an unconfigured instance of this class. The resulting factory
- * may be configured by calling the appropriate setter methods.
- */
- public DiskFileItemFactory() {
- this(DEFAULT_SIZE_THRESHOLD, null);
- }
-
-
- /**
- * 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 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 <code>true</code> if this is a plain form field;
- * <code>false</code> 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) {
- DiskFileItem result = new DiskFileItem(fieldName, contentType,
- isFormField, fileName, sizeThreshold, repository);
- FileCleaningTracker tracker = getFileCleaningTracker();
- if (tracker != null) {
- tracker.track(result.getTempFile(), this);
- }
- return result;
- }
-
-
- /**
- * Returns the tracker, which is responsible for deleting temporary
- * files.
- * @return An instance of {@link FileCleaningTracker}, defaults to
- * {@link org.apache.commons.io.FileCleaner#getInstance()}. Null,
- * if temporary files aren't tracked.
- */
- public FileCleaningTracker getFileCleaningTracker() {
- return fileCleaningTracker;
- }
-
- /**
- * Returns the tracker, which is responsible for deleting temporary
- * files.
- * @param pTracker An instance of {@link FileCleaningTracker},
- * which will from now on track the created files. May be null
- * to disable tracking.
- */
- public void setFileCleaningTracker(FileCleaningTracker pTracker) {
- fileCleaningTracker = pTracker;
- }
-}
+++ /dev/null
-/*
- * 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 org.apache.tomcat.util.http.fileupload;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-
-/**
- * Default implementation of the {@link FileItemHeaders} interface.
- *
- * @author Michael C. Macaluso
- * @since 1.3
- */
-public class FileItemHeadersImpl implements FileItemHeaders, Serializable {
- private static final long serialVersionUID = -4455695752627032559L;
-
- /**
- * Map of <code>String</code> keys to a <code>List</code> of
- * <code>String</code> instances.
- */
- private final Map<String,List<String>> headerNameToValueListMap =
- new HashMap<String,List<String>>();
-
- /**
- * List to preserve order of headers as added. This would not be
- * needed if a <code>LinkedHashMap</code> could be used, but don't
- * want to depend on 1.4.
- */
- private final List<String> headerNameList = new ArrayList<String>();
-
- public String getHeader(String name) {
- String nameLower = name.toLowerCase(Locale.ENGLISH);
- List<String> headerValueList = headerNameToValueListMap.get(nameLower);
- if (null == headerValueList) {
- return null;
- }
- return headerValueList.get(0);
- }
-
- public Iterator<String> getHeaderNames() {
- return headerNameList.iterator();
- }
-
- public Iterator<String> getHeaders(String name) {
- String nameLower = name.toLowerCase(Locale.ENGLISH);
- List<String> headerValueList = headerNameToValueListMap.get(nameLower);
- if (null == headerValueList) {
- return Collections.<String>emptyList().iterator();
- }
- return headerValueList.iterator();
- }
-
- /**
- * Method to add header values to this instance.
- *
- * @param name name of this header
- * @param value value of this header
- */
- public synchronized void addHeader(String name, String value) {
- String nameLower = name.toLowerCase();
- List<String> headerValueList = headerNameToValueListMap.get(nameLower);
- if (null == headerValueList) {
- headerValueList = new ArrayList<String>();
- headerNameToValueListMap.put(nameLower, headerValueList);
- headerNameList.add(nameLower);
- }
- headerValueList.add(value);
- }
-}
import javax.servlet.http.HttpServletRequest;
import org.apache.tomcat.util.http.fileupload.MultipartStream.ItemInputStream;
+import org.apache.tomcat.util.http.fileupload.util.Closeable;
+import org.apache.tomcat.util.http.fileupload.util.FileItemHeadersImpl;
+import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
+import org.apache.tomcat.util.http.fileupload.util.Streams;
/**
+++ /dev/null
-/*
- * 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 org.apache.tomcat.util.http.fileupload;
-
-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 <code>int</code> in the range
- * <code>0</code> to <code>255</code>. If no byte is available
- * because the end of the stream has been reached, the value
- * <code>-1</code> is returned. This method blocks until input data
- * is available, the end of the stream is detected, or an exception
- * is thrown.
- * <p>
- * This method
- * simply performs <code>in.read()</code> and returns the result.
- *
- * @return the next byte of data, or <code>-1</code> if the end of the
- * stream is reached.
- * @exception IOException if an I/O error occurs.
- * @see java.io.FilterInputStream#in
- */
- @Override
- public int read() throws IOException {
- int res = super.read();
- if (res != -1) {
- count++;
- checkLimit();
- }
- return res;
- }
-
- /**
- * Reads up to <code>len</code> bytes of data from this input stream
- * into an array of bytes. If <code>len</code> is not zero, the method
- * blocks until some input is available; otherwise, no
- * bytes are read and <code>0</code> is returned.
- * <p>
- * This method simply performs <code>in.read(b, off, len)</code>
- * and returns the result.
- *
- * @param b the buffer into which the data is read.
- * @param off The start offset in the destination array
- * <code>b</code>.
- * @param len the maximum number of bytes read.
- * @return the total number of bytes read into the buffer, or
- * <code>-1</code> if there is no more data because the end of
- * the stream has been reached.
- * @exception NullPointerException If <code>b</code> is <code>null</code>.
- * @exception IndexOutOfBoundsException If <code>off</code> is negative,
- * <code>len</code> is negative, or <code>len</code> is greater than
- * <code>b.length - off</code>
- * @exception IOException if an I/O error occurs.
- * @see java.io.FilterInputStream#in
- */
- @Override
- 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 <code>in.close()</code>.
- *
- * @exception IOException if an I/O error occurs.
- * @see java.io.FilterInputStream#in
- */
- @Override
- public void close() throws IOException {
- closed = true;
- super.close();
- }
-}
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
+import org.apache.tomcat.util.http.fileupload.util.Closeable;
+import org.apache.tomcat.util.http.fileupload.util.Streams;
+
/**
* <p> Low level API for processing file uploads.
+++ /dev/null
-/*
- * 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 org.apache.tomcat.util.http.fileupload;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Locale;
-
-import javax.servlet.http.HttpServletRequest;
-
-
-/**
- * <p>High level API for processing file uploads.</p>
- *
- * <p>This class handles multiple files per single HTML widget, sent using
- * <code>multipart/mixed</code> encoding type, as specified by
- * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
- * #parseRequest(HttpServletRequest)} to acquire a list of {@link
- * org.apache.commons.fileupload.FileItem}s associated with a given HTML
- * widget.</p>
- *
- * <p>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.</p>
- *
- * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
- * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
- * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
- * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
- * @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 <code>true</code> if the request is multipart;
- * <code>false</code> otherwise.
- */
- public static final boolean isMultipartContent(
- HttpServletRequest request) {
- if (!"post".equals(request.getMethod().toLowerCase(Locale.ENGLISH))) {
- return false;
- }
- String contentType = request.getContentType();
- if (contentType == null) {
- return false;
- }
- if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
- return true;
- }
- return false;
- }
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Constructs an uninitialised instance of this class. A factory must be
- * configured, using <code>setFileItemFactory()</code>, 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 <code>FileItem</code> instances.
- *
- * @see FileUpload#FileUpload()
- * @param fileItemFactory The factory to use for creating file items.
- */
- public ServletFileUpload(FileItemFactory fileItemFactory) {
- super(fileItemFactory);
- }
-
-
- // --------------------------------------------------------- Public methods
-
-
- /**
- * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
- * compliant <code>multipart/form-data</code> stream.
- *
- * @param request The servlet request to be parsed.
- *
- * @return A list of <code>FileItem</code> 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 <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
- * compliant <code>multipart/form-data</code> stream.
- *
- * @param request The servlet request to be parsed.
- *
- * @return An iterator to instances of <code>FileItemStream</code>
- * 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));
- }
-}
+++ /dev/null
-/*
- * 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 org.apache.tomcat.util.http.fileupload;
-
-import java.io.InputStream;
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-
-
-/**
- * <p>Provides access to the request information needed for a request made to
- * an HTTP servlet.</p>
- *
- * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
- *
- * @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.
- */
- @Override
- public String toString() {
- return "ContentLength="
- + this.getContentLength()
- + ", ContentType="
- + this.getContentType();
- }
-}
+++ /dev/null
-/*
- * 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 org.apache.tomcat.util.http.fileupload;
-
-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
- * <pre>
- * copy(pInputStream, pOutputStream, new byte[8192]);
- * </pre>
- * @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 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 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);
- }
-}
--- /dev/null
+/*
+ * 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 org.apache.tomcat.util.http.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.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+
+import org.apache.tomcat.util.http.fileupload.DeferredFileOutputStream;
+import org.apache.tomcat.util.http.fileupload.FileItem;
+import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
+import org.apache.tomcat.util.http.fileupload.FileItemHeadersSupport;
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+import org.apache.tomcat.util.http.fileupload.IOUtils;
+import org.apache.tomcat.util.http.fileupload.ParameterParser;
+
+
+/**
+ * <p> The default implementation of the
+ * {@link org.apache.commons.fileupload.FileItem FileItem} interface.
+ *
+ * <p> After retrieving an instance of this class from a {@link
+ * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
+ * {@link 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.
+ *
+ * <p>When using the <code>DiskFileItemFactory</code>, 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.</p>
+ *
+ * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @author Sean C. Sullivan
+ *
+ * @since FileUpload 1.1
+ *
+ * @version $Id$
+ */
+public class DiskFileItem
+ implements FileItem, FileItemHeadersSupport {
+
+ // ----------------------------------------------------- Manifest constants
+
+ /**
+ * The UID to use when serializing this instance.
+ */
+ private static final long serialVersionUID = 2237570099615271025L;
+
+
+ /**
+ * 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 <code>null</code> 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;
+
+ /**
+ * The temporary file to use.
+ */
+ private transient File tempFile;
+
+ /**
+ * File to allow for serialization of the content of this item.
+ */
+ private File dfosFile;
+
+ /**
+ * The file items headers.
+ */
+ private FileItemHeaders headers;
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Constructs a new <code>DiskFileItem</code> instance.
+ *
+ * @param fieldName The name of the form field.
+ * @param contentType The content type passed by the browser or
+ * <code>null</code> 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
+ * <code>null</code> 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 <code>null</code> if
+ * not defined.
+ *
+ * @return The content type passed by the agent or <code>null</code> if
+ * not defined.
+ */
+ public String getContentType() {
+ return contentType;
+ }
+
+
+ /**
+ * Returns the content charset passed by the agent or <code>null</code> if
+ * not defined.
+ *
+ * @return The content charset passed by the agent or <code>null</code> if
+ * not defined.
+ */
+ public String getCharSet() {
+ ParameterParser parser = new ParameterParser();
+ parser.setLowerCaseNames(true);
+ // Parameter parser can handle null input
+ Map<String,String> params = parser.parse(getContentType(), ';');
+ return 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 <code>true</code> if the file contents will be read
+ * from memory; <code>false</code> 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.
+ * <p>
+ * 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.
+ * <p>
+ * This method is only guaranteed to work <em>once</em>, 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 <code>File</code> 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 <code>FileItem</code> 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 <code>FileItem</code> instance represents
+ * a simple form field.
+ *
+ * @return <code>true</code> if the instance represents a simple form
+ * field; <code>false</code> if it represents an uploaded file.
+ *
+ * @see #setFormField(boolean)
+ *
+ */
+ public boolean isFormField() {
+ return isFormField;
+ }
+
+
+ /**
+ * Specifies whether or not a <code>FileItem</code> instance represents
+ * a simple form field.
+ *
+ * @param state <code>true</code> if the instance represents a simple form
+ * field; <code>false</code> 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 <code>FileItem</code>'s
+ * data's temporary location on the disk. Note that for
+ * <code>FileItem</code>s that have their data stored in memory,
+ * this method will return <code>null</code>. 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 <code>null</code> if the data is stored in
+ * memory.
+ */
+ public File getStoreLocation() {
+ return dfos == null ? null : dfos.getFile();
+ }
+
+
+ // ------------------------------------------------------ Protected methods
+
+
+ /**
+ * Removes the file contents from the temporary storage.
+ */
+ @Override
+ 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 <code>FileItem</code> 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() {
+ if (tempFile == null) {
+ File tempDir = repository;
+ if (tempDir == null) {
+ tempDir = new File(System.getProperty("java.io.tmpdir"));
+ }
+
+ String tempFileName =
+ "upload_" + UID + "_" + getUniqueId() + ".tmp";
+
+ tempFile = new File(tempDir, tempFileName);
+ }
+ return tempFile;
+ }
+
+
+ // -------------------------------------------------------- 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.
+ */
+ @Override
+ 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;
+ }
+
+ /**
+ * Returns the file item headers.
+ * @return The file items headers.
+ */
+ public FileItemHeaders getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Sets the file item headers.
+ * @param pHeaders The file items headers.
+ */
+ public void setHeaders(FileItemHeaders pHeaders) {
+ headers = pHeaders;
+ }
+}
--- /dev/null
+/*
+ * 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 org.apache.tomcat.util.http.fileupload.disk;
+
+import java.io.File;
+
+import org.apache.tomcat.util.http.fileupload.FileCleaningTracker;
+import org.apache.tomcat.util.http.fileupload.FileItem;
+import org.apache.tomcat.util.http.fileupload.FileItemFactory;
+
+
+/**
+ * <p>The default {@link org.apache.commons.fileupload.FileItemFactory}
+ * implementation. This implementation creates
+ * {@link 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.</p>
+ *
+ * <p>If not otherwise configured, the default configuration values are as
+ * follows:
+ * <ul>
+ * <li>Size threshold is 10KB.</li>
+ * <li>Repository is the system default temp directory, as returned by
+ * <code>System.getProperty("java.io.tmpdir")</code>.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>When using the <code>DiskFileItemFactory</code>, 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.)
+ * Cleaning up those files is done by an instance of
+ * {@link FileCleaningTracker}, and an associated thread. In a complex
+ * environment, for example in a web application, you should consider
+ * terminating this thread, for example, when your web application
+ * ends. See the section on "Resource cleanup"
+ * in the users guide of commons-fileupload.</p>
+ *
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ *
+ * @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;
+
+
+ /**
+ * <p>The instance of {@link FileCleaningTracker}, which is responsible
+ * for deleting temporary files.</p>
+ * <p>May be null, if tracking files is not required.</p>
+ */
+ private FileCleaningTracker fileCleaningTracker;
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Constructs an unconfigured instance of this class. The resulting factory
+ * may be configured by calling the appropriate setter methods.
+ */
+ public DiskFileItemFactory() {
+ this(DEFAULT_SIZE_THRESHOLD, null);
+ }
+
+
+ /**
+ * 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 org.apache.tomcat.util.http.fileupload.disk.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 <code>true</code> if this is a plain form field;
+ * <code>false</code> 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) {
+ DiskFileItem result = new DiskFileItem(fieldName, contentType,
+ isFormField, fileName, sizeThreshold, repository);
+ FileCleaningTracker tracker = getFileCleaningTracker();
+ if (tracker != null) {
+ tracker.track(result.getTempFile(), this);
+ }
+ return result;
+ }
+
+
+ /**
+ * Returns the tracker, which is responsible for deleting temporary
+ * files.
+ * @return An instance of {@link FileCleaningTracker}, defaults to
+ * {@link org.apache.commons.io.FileCleaner#getInstance()}. Null,
+ * if temporary files aren't tracked.
+ */
+ public FileCleaningTracker getFileCleaningTracker() {
+ return fileCleaningTracker;
+ }
+
+ /**
+ * Returns the tracker, which is responsible for deleting temporary
+ * files.
+ * @param pTracker An instance of {@link FileCleaningTracker},
+ * which will from now on track the created files. May be null
+ * to disable tracking.
+ */
+ public void setFileCleaningTracker(FileCleaningTracker pTracker) {
+ fileCleaningTracker = pTracker;
+ }
+}
--- /dev/null
+/*
+ * 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 org.apache.tomcat.util.http.fileupload.servlet;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tomcat.util.http.fileupload.FileItem;
+import org.apache.tomcat.util.http.fileupload.FileItemFactory;
+import org.apache.tomcat.util.http.fileupload.FileItemIterator;
+import org.apache.tomcat.util.http.fileupload.FileUpload;
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+
+/**
+ * <p>High level API for processing file uploads.</p>
+ *
+ * <p>This class handles multiple files per single HTML widget, sent using
+ * <code>multipart/mixed</code> encoding type, as specified by
+ * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
+ * #parseRequest(HttpServletRequest)} to acquire a list of {@link
+ * org.apache.commons.fileupload.FileItem}s associated with a given HTML
+ * widget.</p>
+ *
+ * <p>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.</p>
+ *
+ * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
+ * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ * @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 <code>true</code> if the request is multipart;
+ * <code>false</code> otherwise.
+ */
+ public static final boolean isMultipartContent(
+ HttpServletRequest request) {
+ if (!"post".equals(request.getMethod().toLowerCase(Locale.ENGLISH))) {
+ return false;
+ }
+ String contentType = request.getContentType();
+ if (contentType == null) {
+ return false;
+ }
+ if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
+ return true;
+ }
+ return false;
+ }
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Constructs an uninitialised instance of this class. A factory must be
+ * configured, using <code>setFileItemFactory()</code>, 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 <code>FileItem</code> instances.
+ *
+ * @see FileUpload#FileUpload()
+ * @param fileItemFactory The factory to use for creating file items.
+ */
+ public ServletFileUpload(FileItemFactory fileItemFactory) {
+ super(fileItemFactory);
+ }
+
+
+ // --------------------------------------------------------- Public methods
+
+
+ /**
+ * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
+ * compliant <code>multipart/form-data</code> stream.
+ *
+ * @param request The servlet request to be parsed.
+ *
+ * @return A list of <code>FileItem</code> 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 <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
+ * compliant <code>multipart/form-data</code> stream.
+ *
+ * @param request The servlet request to be parsed.
+ *
+ * @return An iterator to instances of <code>FileItemStream</code>
+ * 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));
+ }
+}
--- /dev/null
+/*
+ * 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 org.apache.tomcat.util.http.fileupload.servlet;
+
+import java.io.InputStream;
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tomcat.util.http.fileupload.RequestContext;
+
+
+/**
+ * <p>Provides access to the request information needed for a request made to
+ * an HTTP servlet.</p>
+ *
+ * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
+ *
+ * @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.
+ */
+ @Override
+ public String toString() {
+ return "ContentLength="
+ + this.getContentLength()
+ + ", ContentType="
+ + this.getContentType();
+ }
+}
--- /dev/null
+/*
+ * 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 org.apache.tomcat.util.http.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;
+}
--- /dev/null
+/*
+ * 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 org.apache.tomcat.util.http.fileupload.util;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
+
+
+/**
+ * Default implementation of the {@link FileItemHeaders} interface.
+ *
+ * @author Michael C. Macaluso
+ * @since 1.3
+ */
+public class FileItemHeadersImpl implements FileItemHeaders, Serializable {
+ private static final long serialVersionUID = -4455695752627032559L;
+
+ /**
+ * Map of <code>String</code> keys to a <code>List</code> of
+ * <code>String</code> instances.
+ */
+ private final Map<String,List<String>> headerNameToValueListMap =
+ new HashMap<String,List<String>>();
+
+ /**
+ * List to preserve order of headers as added. This would not be
+ * needed if a <code>LinkedHashMap</code> could be used, but don't
+ * want to depend on 1.4.
+ */
+ private final List<String> headerNameList = new ArrayList<String>();
+
+ public String getHeader(String name) {
+ String nameLower = name.toLowerCase(Locale.ENGLISH);
+ List<String> headerValueList = headerNameToValueListMap.get(nameLower);
+ if (null == headerValueList) {
+ return null;
+ }
+ return headerValueList.get(0);
+ }
+
+ public Iterator<String> getHeaderNames() {
+ return headerNameList.iterator();
+ }
+
+ public Iterator<String> getHeaders(String name) {
+ String nameLower = name.toLowerCase(Locale.ENGLISH);
+ List<String> headerValueList = headerNameToValueListMap.get(nameLower);
+ if (null == headerValueList) {
+ return Collections.<String>emptyList().iterator();
+ }
+ return headerValueList.iterator();
+ }
+
+ /**
+ * Method to add header values to this instance.
+ *
+ * @param name name of this header
+ * @param value value of this header
+ */
+ public synchronized void addHeader(String name, String value) {
+ String nameLower = name.toLowerCase();
+ List<String> headerValueList = headerNameToValueListMap.get(nameLower);
+ if (null == headerValueList) {
+ headerValueList = new ArrayList<String>();
+ headerNameToValueListMap.put(nameLower, headerValueList);
+ headerNameList.add(nameLower);
+ }
+ headerValueList.add(value);
+ }
+}
--- /dev/null
+/*
+ * 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 org.apache.tomcat.util.http.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 <code>int</code> in the range
+ * <code>0</code> to <code>255</code>. If no byte is available
+ * because the end of the stream has been reached, the value
+ * <code>-1</code> is returned. This method blocks until input data
+ * is available, the end of the stream is detected, or an exception
+ * is thrown.
+ * <p>
+ * This method
+ * simply performs <code>in.read()</code> and returns the result.
+ *
+ * @return the next byte of data, or <code>-1</code> if the end of the
+ * stream is reached.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterInputStream#in
+ */
+ @Override
+ public int read() throws IOException {
+ int res = super.read();
+ if (res != -1) {
+ count++;
+ checkLimit();
+ }
+ return res;
+ }
+
+ /**
+ * Reads up to <code>len</code> bytes of data from this input stream
+ * into an array of bytes. If <code>len</code> is not zero, the method
+ * blocks until some input is available; otherwise, no
+ * bytes are read and <code>0</code> is returned.
+ * <p>
+ * This method simply performs <code>in.read(b, off, len)</code>
+ * and returns the result.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off The start offset in the destination array
+ * <code>b</code>.
+ * @param len the maximum number of bytes read.
+ * @return the total number of bytes read into the buffer, or
+ * <code>-1</code> if there is no more data because the end of
+ * the stream has been reached.
+ * @exception NullPointerException If <code>b</code> is <code>null</code>.
+ * @exception IndexOutOfBoundsException If <code>off</code> is negative,
+ * <code>len</code> is negative, or <code>len</code> is greater than
+ * <code>b.length - off</code>
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterInputStream#in
+ */
+ @Override
+ 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 <code>in.close()</code>.
+ *
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterInputStream#in
+ */
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ super.close();
+ }
+}
--- /dev/null
+/*
+ * 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 org.apache.tomcat.util.http.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
+ * <pre>
+ * copy(pInputStream, pOutputStream, new byte[8192]);
+ * </pre>
+ * @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 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 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);
+ }
+}