Call super.finalize() in a finalize() implementation. (As suggested by
Fortify static code analysis.)
diff --git a/src/main/java/org/apache/commons/fileupload2/disk/DiskFileItem.java b/src/main/java/org/apache/commons/fileupload2/disk/DiskFileItem.java
index 7bc224a..b76bb75 100644
--- a/src/main/java/org/apache/commons/fileupload2/disk/DiskFileItem.java
+++ b/src/main/java/org/apache/commons/fileupload2/disk/DiskFileItem.java
@@ -1,650 +1,651 @@
-/*
- * 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.commons.fileupload2.disk;
-
-import static java.lang.String.format;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.commons.fileupload2.FileItem;
-import org.apache.commons.fileupload2.FileItemHeaders;
-import org.apache.commons.fileupload2.FileUploadException;
-import org.apache.commons.fileupload2.ParameterParser;
-import org.apache.commons.fileupload2.util.Streams;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.DeferredFileOutputStream;
-
-/**
- * <p> The default implementation of the
- * {@link org.apache.commons.fileupload2.FileItem FileItem} interface.
- *
- * <p> After retrieving an instance of this class from a {@link
- * DiskFileItemFactory} instance (see
- * {@link org.apache.commons.fileupload2.servlet.ServletFileUpload
- * #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>Temporary files, which are created for file items, should be
- * deleted later on. The best way to do this is using a
- * {@link org.apache.commons.io.FileCleaningTracker}, which you can set on the
- * {@link DiskFileItemFactory}. However, if you do use such a tracker,
- * then you must 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 and stopped
- * automatically by the {@link org.apache.commons.io.FileCleaningTracker} when
- * there are files to be tracked.
- * 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>
- *
- * @since FileUpload 1.1
- */
-public class DiskFileItem
- implements FileItem {
-
- // ----------------------------------------------------- Manifest constants
-
- /**
- * Default content charset to be used when no explicit charset
- * parameter is provided by the sender. Media subtypes of the
- * "text" type are defined to have a default charset value of
- * "ISO-8859-1" when received via HTTP.
- */
- public static final String DEFAULT_CHARSET = "ISO-8859-1";
-
- // ----------------------------------------------------------- Data members
-
- /**
- * UID used in unique file name generation.
- */
- private static final String UID =
- UUID.randomUUID().toString().replace('-', '_');
-
- /**
- * Counter used in unique identifier generation.
- */
- private static final AtomicInteger COUNTER = new AtomicInteger(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 final String contentType;
-
- /**
- * Whether or not this item is a simple form field.
- */
- private boolean isFormField;
-
- /**
- * The original file name in the user's file system.
- */
- private final 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 final int sizeThreshold;
-
- /**
- * The directory in which uploaded files will be stored, if stored on disk.
- */
- private final 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;
-
- /**
- * The file items headers.
- */
- private FileItemHeaders headers;
-
- /**
- * Default content charset to be used when no explicit charset
- * parameter is provided by the sender.
- */
- private String defaultCharset = DEFAULT_CHARSET;
-
- // ----------------------------------------------------------- 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 file name in the user's file system, 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.
- */
- @Override
- 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.
- */
- @Override
- 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 file name in the client's file system.
- *
- * @return The original file name in the client's file system.
- * @throws org.apache.commons.fileupload2.InvalidFileNameException The file name contains a NUL character,
- * which might be an indicator of a security attack. If you intend to
- * use the file name anyways, catch the exception and use
- * {@link org.apache.commons.fileupload2.InvalidFileNameException#getName()}.
- */
- @Override
- public String getName() {
- return Streams.checkFileName(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.
- */
- @Override
- 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.
- */
- @Override
- 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
- * or {@code null} if the data cannot be read
- */
- @Override
- public byte[] get() {
- if (isInMemory()) {
- if (cachedContent == null && dfos != null) {
- cachedContent = dfos.getData();
- }
- return cachedContent;
- }
-
- byte[] fileData = new byte[(int) getSize()];
- InputStream fis = null;
-
- try {
- fis = new FileInputStream(dfos.getFile());
- IOUtils.readFully(fis, fileData);
- } catch (IOException e) {
- fileData = null;
- } finally {
- IOUtils.closeQuietly(fis);
- }
-
- 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.
- */
- @Override
- 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.
- *
- * <b>TODO</b> Consider making this method throw UnsupportedEncodingException.
- *
- * @return The contents of the file, as a string.
- */
- @Override
- public String getString() {
- byte[] rawdata = get();
- String charset = getCharSet();
- if (charset == null) {
- charset = defaultCharset;
- }
- 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.
- */
- @Override
- public void write(File file) throws Exception {
- if (isInMemory()) {
- FileOutputStream fout = null;
- try {
- fout = new FileOutputStream(file);
- fout.write(get());
- fout.close();
- } finally {
- IOUtils.closeQuietly(fout);
- }
- } 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 (file.exists()) {
- if (!file.delete()) {
- throw new FileUploadException(
- "Cannot write uploaded file to disk!");
- }
- }
- FileUtils.moveFile(outputFile, file);
- } 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.
- */
- @Override
- public void delete() {
- cachedContent = null;
- File outputFile = getStoreLocation();
- if (outputFile != null && !isInMemory() && 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)
- *
- */
- @Override
- 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()
- *
- */
- @Override
- 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)
- *
- */
- @Override
- 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()
- *
- */
- @Override
- 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 contents of the file.
- *
- * @throws IOException if an error occurs.
- */
- @Override
- 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() {
- if (dfos == null) {
- return null;
- }
- if (isInMemory()) {
- return null;
- }
- return dfos.getFile();
- }
-
- // ------------------------------------------------------ Protected methods
-
- /**
- * Removes the file contents from the temporary storage.
- */
- @Override
- protected void finalize() {
- if (dfos == null || dfos.isInMemory()) {
- return;
- }
- 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.
- * <p>
- * <b>Note: Subclasses that override this method must ensure that they return the
- * same File each time.</b>
- *
- * @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 = format("upload_%s_%s.tmp", UID, getUniqueId());
-
- 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 appearance.
- *
- * @return A String with the non-random looking instance identifier.
- */
- private static String getUniqueId() {
- final int limit = 100000000;
- int current = COUNTER.getAndIncrement();
- 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 format("name=%s, StoreLocation=%s, size=%s bytes, isFormField=%s, FieldName=%s",
- getName(), getStoreLocation(), Long.valueOf(getSize()),
- Boolean.valueOf(isFormField()), getFieldName());
- }
-
- /**
- * Returns the file item headers.
- * @return The file items headers.
- */
- @Override
- public FileItemHeaders getHeaders() {
- return headers;
- }
-
- /**
- * Sets the file item headers.
- * @param pHeaders The file items headers.
- */
- @Override
- public void setHeaders(FileItemHeaders pHeaders) {
- headers = pHeaders;
- }
-
- /**
- * Returns the default charset for use when no explicit charset
- * parameter is provided by the sender.
- * @return the default charset
- */
- public String getDefaultCharset() {
- return defaultCharset;
- }
-
- /**
- * Sets the default charset for use when no explicit charset
- * parameter is provided by the sender.
- * @param charset the default charset
- */
- public void setDefaultCharset(String charset) {
- defaultCharset = charset;
- }
-}
+/*
+ * 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.commons.fileupload2.disk;
+
+import static java.lang.String.format;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.fileupload2.FileItem;
+import org.apache.commons.fileupload2.FileItemHeaders;
+import org.apache.commons.fileupload2.FileUploadException;
+import org.apache.commons.fileupload2.ParameterParser;
+import org.apache.commons.fileupload2.util.Streams;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.DeferredFileOutputStream;
+
+/**
+ * <p> The default implementation of the
+ * {@link org.apache.commons.fileupload2.FileItem FileItem} interface.
+ *
+ * <p> After retrieving an instance of this class from a {@link
+ * DiskFileItemFactory} instance (see
+ * {@link org.apache.commons.fileupload2.servlet.ServletFileUpload
+ * #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>Temporary files, which are created for file items, should be
+ * deleted later on. The best way to do this is using a
+ * {@link org.apache.commons.io.FileCleaningTracker}, which you can set on the
+ * {@link DiskFileItemFactory}. However, if you do use such a tracker,
+ * then you must 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 and stopped
+ * automatically by the {@link org.apache.commons.io.FileCleaningTracker} when
+ * there are files to be tracked.
+ * 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>
+ *
+ * @since FileUpload 1.1
+ */
+public class DiskFileItem
+ implements FileItem {
+
+ // ----------------------------------------------------- Manifest constants
+
+ /**
+ * Default content charset to be used when no explicit charset
+ * parameter is provided by the sender. Media subtypes of the
+ * "text" type are defined to have a default charset value of
+ * "ISO-8859-1" when received via HTTP.
+ */
+ public static final String DEFAULT_CHARSET = "ISO-8859-1";
+
+ // ----------------------------------------------------------- Data members
+
+ /**
+ * UID used in unique file name generation.
+ */
+ private static final String UID =
+ UUID.randomUUID().toString().replace('-', '_');
+
+ /**
+ * Counter used in unique identifier generation.
+ */
+ private static final AtomicInteger COUNTER = new AtomicInteger(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 final String contentType;
+
+ /**
+ * Whether or not this item is a simple form field.
+ */
+ private boolean isFormField;
+
+ /**
+ * The original file name in the user's file system.
+ */
+ private final 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 final int sizeThreshold;
+
+ /**
+ * The directory in which uploaded files will be stored, if stored on disk.
+ */
+ private final 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;
+
+ /**
+ * The file items headers.
+ */
+ private FileItemHeaders headers;
+
+ /**
+ * Default content charset to be used when no explicit charset
+ * parameter is provided by the sender.
+ */
+ private String defaultCharset = DEFAULT_CHARSET;
+
+ // ----------------------------------------------------------- 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 file name in the user's file system, 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.
+ */
+ @Override
+ 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.
+ */
+ @Override
+ 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 file name in the client's file system.
+ *
+ * @return The original file name in the client's file system.
+ * @throws org.apache.commons.fileupload2.InvalidFileNameException The file name contains a NUL character,
+ * which might be an indicator of a security attack. If you intend to
+ * use the file name anyways, catch the exception and use
+ * {@link org.apache.commons.fileupload2.InvalidFileNameException#getName()}.
+ */
+ @Override
+ public String getName() {
+ return Streams.checkFileName(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.
+ */
+ @Override
+ 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.
+ */
+ @Override
+ 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
+ * or {@code null} if the data cannot be read
+ */
+ @Override
+ public byte[] get() {
+ if (isInMemory()) {
+ if (cachedContent == null && dfos != null) {
+ cachedContent = dfos.getData();
+ }
+ return cachedContent;
+ }
+
+ byte[] fileData = new byte[(int) getSize()];
+ InputStream fis = null;
+
+ try {
+ fis = new FileInputStream(dfos.getFile());
+ IOUtils.readFully(fis, fileData);
+ } catch (IOException e) {
+ fileData = null;
+ } finally {
+ IOUtils.closeQuietly(fis);
+ }
+
+ 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.
+ */
+ @Override
+ 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.
+ *
+ * <b>TODO</b> Consider making this method throw UnsupportedEncodingException.
+ *
+ * @return The contents of the file, as a string.
+ */
+ @Override
+ public String getString() {
+ byte[] rawdata = get();
+ String charset = getCharSet();
+ if (charset == null) {
+ charset = defaultCharset;
+ }
+ 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.
+ */
+ @Override
+ public void write(File file) throws Exception {
+ if (isInMemory()) {
+ FileOutputStream fout = null;
+ try {
+ fout = new FileOutputStream(file);
+ fout.write(get());
+ fout.close();
+ } finally {
+ IOUtils.closeQuietly(fout);
+ }
+ } 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 (file.exists()) {
+ if (!file.delete()) {
+ throw new FileUploadException(
+ "Cannot write uploaded file to disk!");
+ }
+ }
+ FileUtils.moveFile(outputFile, file);
+ } 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.
+ */
+ @Override
+ public void delete() {
+ cachedContent = null;
+ File outputFile = getStoreLocation();
+ if (outputFile != null && !isInMemory() && 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)
+ *
+ */
+ @Override
+ 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()
+ *
+ */
+ @Override
+ 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)
+ *
+ */
+ @Override
+ 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()
+ *
+ */
+ @Override
+ 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 contents of the file.
+ *
+ * @throws IOException if an error occurs.
+ */
+ @Override
+ 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() {
+ if (dfos == null) {
+ return null;
+ }
+ if (isInMemory()) {
+ return null;
+ }
+ return dfos.getFile();
+ }
+
+ // ------------------------------------------------------ Protected methods
+
+ /**
+ * Removes the file contents from the temporary storage.
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ if (dfos == null || dfos.isInMemory()) {
+ return;
+ }
+ File outputFile = dfos.getFile();
+
+ if (outputFile != null && outputFile.exists()) {
+ outputFile.delete();
+ }
+ super.finalize();
+ }
+
+ /**
+ * 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.
+ * <p>
+ * <b>Note: Subclasses that override this method must ensure that they return the
+ * same File each time.</b>
+ *
+ * @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 = format("upload_%s_%s.tmp", UID, getUniqueId());
+
+ 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 appearance.
+ *
+ * @return A String with the non-random looking instance identifier.
+ */
+ private static String getUniqueId() {
+ final int limit = 100000000;
+ int current = COUNTER.getAndIncrement();
+ 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 format("name=%s, StoreLocation=%s, size=%s bytes, isFormField=%s, FieldName=%s",
+ getName(), getStoreLocation(), Long.valueOf(getSize()),
+ Boolean.valueOf(isFormField()), getFieldName());
+ }
+
+ /**
+ * Returns the file item headers.
+ * @return The file items headers.
+ */
+ @Override
+ public FileItemHeaders getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Sets the file item headers.
+ * @param pHeaders The file items headers.
+ */
+ @Override
+ public void setHeaders(FileItemHeaders pHeaders) {
+ headers = pHeaders;
+ }
+
+ /**
+ * Returns the default charset for use when no explicit charset
+ * parameter is provided by the sender.
+ * @return the default charset
+ */
+ public String getDefaultCharset() {
+ return defaultCharset;
+ }
+
+ /**
+ * Sets the default charset for use when no explicit charset
+ * parameter is provided by the sender.
+ * @param charset the default charset
+ */
+ public void setDefaultCharset(String charset) {
+ defaultCharset = charset;
+ }
+}