DiskFileItem.java
Upload User: gdxydsw
Upload Date: 2019-01-29
Package Size: 16721k
Code Size: 19k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * Copyright 2001-2004 The Apache Software Foundation
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *     http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package net.jforum.util.legacy.commons.fileupload.disk;
  17. import java.io.BufferedInputStream;
  18. import java.io.BufferedOutputStream;
  19. import java.io.ByteArrayInputStream;
  20. import java.io.File;
  21. import java.io.FileInputStream;
  22. import java.io.FileOutputStream;
  23. import java.io.IOException;
  24. import java.io.InputStream;
  25. import java.io.OutputStream;
  26. import java.io.UnsupportedEncodingException;
  27. import java.util.Map;
  28. import net.jforum.util.legacy.commons.fileupload.FileItem;
  29. import net.jforum.util.legacy.commons.fileupload.FileUploadException;
  30. import net.jforum.util.legacy.commons.fileupload.ParameterParser;
  31. import org.apache.commons.io.FileCleaner;
  32. import org.apache.commons.io.output.DeferredFileOutputStream;
  33. /**
  34.  * <p> The default implementation of the
  35.  * {@link org.apache.commons.fileupload.FileItem FileItem} interface.
  36.  *
  37.  * <p> After retrieving an instance of this class from a {@link
  38.  * org.apache.commons.fileupload.DiskFileUpload DiskFileUpload} instance (see
  39.  * {@link org.apache.commons.fileupload.DiskFileUpload
  40.  * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may
  41.  * either request all contents of file at once using {@link #get()} or
  42.  * request an {@link java.io.InputStream InputStream} with
  43.  * {@link #getInputStream()} and process the file without attempting to load
  44.  * it into memory, which may come handy with large files.
  45.  *
  46.  * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
  47.  * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
  48.  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
  49.  * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
  50.  * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
  51.  * @author Sean C. Sullivan
  52.  *
  53.  * @since FileUpload 1.1
  54.  *
  55.  * @version $Id: DiskFileItem.java,v 1.3 2005/07/26 03:05:02 rafaelsteil Exp $
  56.  */
  57. public class DiskFileItem
  58.     implements FileItem {
  59.     // ----------------------------------------------------- Manifest constants
  60.     /**
  61.      * Default content charset to be used when no explicit charset
  62.      * parameter is provided by the sender. Media subtypes of the
  63.      * "text" type are defined to have a default charset value of
  64.      * "ISO-8859-1" when received via HTTP.
  65.      */
  66.     public static final String DEFAULT_CHARSET = "ISO-8859-1";
  67.     /**
  68.      * Size of buffer to use when writing an item to disk.
  69.      */
  70.     private static final int WRITE_BUFFER_SIZE = 2048;
  71.     // ----------------------------------------------------------- Data members
  72.     /**
  73.      * Counter used in unique identifier generation.
  74.      */
  75.     private static int counter = 0;
  76.     /**
  77.      * The name of the form field as provided by the browser.
  78.      */
  79.     private String fieldName;
  80.     /**
  81.      * The content type passed by the browser, or <code>null</code> if
  82.      * not defined.
  83.      */
  84.     private String contentType;
  85.     /**
  86.      * Whether or not this item is a simple form field.
  87.      */
  88.     private boolean isFormField;
  89.     /**
  90.      * The original filename in the user's filesystem.
  91.      */
  92.     private String fileName;
  93.     /**
  94.      * The threshold above which uploads will be stored on disk.
  95.      */
  96.     private int sizeThreshold;
  97.     /**
  98.      * The directory in which uploaded files will be stored, if stored on disk.
  99.      */
  100.     private File repository;
  101.     /**
  102.      * Cached contents of the file.
  103.      */
  104.     private byte[] cachedContent;
  105.     /**
  106.      * Output stream for this item.
  107.      */
  108.     private DeferredFileOutputStream dfos;
  109.     // ----------------------------------------------------------- Constructors
  110.     /**
  111.      * Constructs a new <code>DiskFileItem</code> instance.
  112.      *
  113.      * @param fieldName     The name of the form field.
  114.      * @param contentType   The content type passed by the browser or
  115.      *                      <code>null</code> if not specified.
  116.      * @param isFormField   Whether or not this item is a plain form field, as
  117.      *                      opposed to a file upload.
  118.      * @param fileName      The original filename in the user's filesystem, or
  119.      *                      <code>null</code> if not specified.
  120.      * @param sizeThreshold The threshold, in bytes, below which items will be
  121.      *                      retained in memory and above which they will be
  122.      *                      stored as a file.
  123.      * @param repository    The data repository, which is the directory in
  124.      *                      which files will be created, should the item size
  125.      *                      exceed the threshold.
  126.      */
  127.     public DiskFileItem(String fieldName, String contentType,
  128.             boolean isFormField, String fileName, int sizeThreshold,
  129.             File repository) {
  130.         this.fieldName = fieldName;
  131.         this.contentType = contentType;
  132.         this.isFormField = isFormField;
  133.         this.fileName = fileName;
  134.         this.sizeThreshold = sizeThreshold;
  135.         this.repository = repository;
  136.     }
  137.     // ------------------------------- Methods from javax.activation.DataSource
  138.     /**
  139.      * Returns an {@link java.io.InputStream InputStream} that can be
  140.      * used to retrieve the contents of the file.
  141.      *
  142.      * @return An {@link java.io.InputStream InputStream} that can be
  143.      *         used to retrieve the contents of the file.
  144.      *
  145.      * @exception IOException if an error occurs.
  146.      */
  147.     public InputStream getInputStream()
  148.         throws IOException {
  149.         if (!dfos.isInMemory()) {
  150.             return new FileInputStream(dfos.getFile());
  151.         }
  152.         if (cachedContent == null) {
  153.             cachedContent = dfos.getData();
  154.         }
  155.         return new ByteArrayInputStream(cachedContent);
  156.     }
  157.     /**
  158.      * Returns the content type passed by the agent or <code>null</code> if
  159.      * not defined.
  160.      *
  161.      * @return The content type passed by the agent or <code>null</code> if
  162.      *         not defined.
  163.      */
  164.     public String getContentType() {
  165.         return contentType;
  166.     }
  167.     /**
  168.      * Returns the content charset passed by the agent or <code>null</code> if
  169.      * not defined.
  170.      *
  171.      * @return The content charset passed by the agent or <code>null</code> if
  172.      *         not defined.
  173.      */
  174.     public String getCharSet() {
  175.         ParameterParser parser = new ParameterParser();
  176.         parser.setLowerCaseNames(true);
  177.         // Parameter parser can handle null input
  178.         Map params = parser.parse(getContentType(), ';');
  179.         return (String) params.get("charset");
  180.     }
  181.     /**
  182.      * Returns the original filename in the client's filesystem.
  183.      *
  184.      * @return The original filename in the client's filesystem.
  185.      */
  186.     public String getName() {
  187.         return fileName;
  188.     }
  189.     // ------------------------------------------------------- FileItem methods
  190.     /**
  191.      * Provides a hint as to whether or not the file contents will be read
  192.      * from memory.
  193.      *
  194.      * @return <code>true</code> if the file contents will be read
  195.      *         from memory; <code>false</code> otherwise.
  196.      */
  197.     public boolean isInMemory() {
  198.         return (dfos.isInMemory());
  199.     }
  200.     /**
  201.      * Returns the size of the file.
  202.      *
  203.      * @return The size of the file, in bytes.
  204.      */
  205.     public long getSize() {
  206.         if (cachedContent != null) {
  207.             return cachedContent.length;
  208.         } else if (dfos.isInMemory()) {
  209.             return dfos.getData().length;
  210.         } else {
  211.             return dfos.getFile().length();
  212.         }
  213.     }
  214.     /**
  215.      * Returns the contents of the file as an array of bytes.  If the
  216.      * contents of the file were not yet cached in memory, they will be
  217.      * loaded from the disk storage and cached.
  218.      *
  219.      * @return The contents of the file as an array of bytes.
  220.      */
  221.     public byte[] get() {
  222.         if (dfos.isInMemory()) {
  223.             if (cachedContent == null) {
  224.                 cachedContent = dfos.getData();
  225.             }
  226.             return cachedContent;
  227.         }
  228.         byte[] fileData = new byte[(int) getSize()];
  229.         FileInputStream fis = null;
  230.         try {
  231.             fis = new FileInputStream(dfos.getFile());
  232.             fis.read(fileData);
  233.         } catch (IOException e) {
  234.             fileData = null;
  235.         } finally {
  236.             if (fis != null) {
  237.                 try {
  238.                     fis.close();
  239.                 } catch (IOException e) {
  240.                     // ignore
  241.                 }
  242.             }
  243.         }
  244.         return fileData;
  245.     }
  246.     /**
  247.      * Returns the contents of the file as a String, using the specified
  248.      * encoding.  This method uses {@link #get()} to retrieve the
  249.      * contents of the file.
  250.      *
  251.      * @param charset The charset to use.
  252.      *
  253.      * @return The contents of the file, as a string.
  254.      *
  255.      * @exception UnsupportedEncodingException if the requested character
  256.      *                                         encoding is not available.
  257.      */
  258.     public String getString(final String charset)
  259.         throws UnsupportedEncodingException {
  260.         return new String(get(), charset);
  261.     }
  262.     /**
  263.      * Returns the contents of the file as a String, using the default
  264.      * character encoding.  This method uses {@link #get()} to retrieve the
  265.      * contents of the file.
  266.      *
  267.      * @return The contents of the file, as a string.
  268.      *
  269.      * @todo Consider making this method throw UnsupportedEncodingException.
  270.      */
  271.     public String getString() {
  272.         byte[] rawdata = get();
  273.         String charset = getCharSet();
  274.         if (charset == null) {
  275.             charset = DEFAULT_CHARSET;
  276.         }
  277.         try {
  278.             return new String(rawdata, charset);
  279.         } catch (UnsupportedEncodingException e) {
  280.             return new String(rawdata);
  281.         }
  282.     }
  283.     /**
  284.      * A convenience method to write an uploaded item to disk. The client code
  285.      * is not concerned with whether or not the item is stored in memory, or on
  286.      * disk in a temporary location. They just want to write the uploaded item
  287.      * to a file.
  288.      * <p>
  289.      * This implementation first attempts to rename the uploaded item to the
  290.      * specified destination file, if the item was originally written to disk.
  291.      * Otherwise, the data will be copied to the specified file.
  292.      * <p>
  293.      * This method is only guaranteed to work <em>once</em>, the first time it
  294.      * is invoked for a particular item. This is because, in the event that the
  295.      * method renames a temporary file, that file will no longer be available
  296.      * to copy or rename again at a later time.
  297.      *
  298.      * @param file The <code>File</code> into which the uploaded item should
  299.      *             be stored.
  300.      *
  301.      * @exception Exception if an error occurs.
  302.      */
  303.     public void write(File file) throws Exception {
  304.         if (isInMemory()) {
  305.             FileOutputStream fout = null;
  306.             try {
  307.                 fout = new FileOutputStream(file);
  308.                 fout.write(get());
  309.             } finally {
  310.                 if (fout != null) {
  311.                     fout.close();
  312.                 }
  313.             }
  314.         } else {
  315.             File outputFile = getStoreLocation();
  316.             if (outputFile != null) {
  317.                 /*
  318.                  * The uploaded file is being stored on disk
  319.                  * in a temporary location so move it to the
  320.                  * desired file.
  321.                  */
  322.                 if (!outputFile.renameTo(file)) {
  323.                     BufferedInputStream in = null;
  324.                     BufferedOutputStream out = null;
  325.                     try {
  326.                         in = new BufferedInputStream(
  327.                             new FileInputStream(outputFile));
  328.                         out = new BufferedOutputStream(
  329.                                 new FileOutputStream(file));
  330.                         byte[] bytes = new byte[WRITE_BUFFER_SIZE];
  331.                         int s = 0;
  332.                         while ((s = in.read(bytes)) != -1) {
  333.                             out.write(bytes, 0, s);
  334.                         }
  335.                     } finally {
  336.                         if (in != null) {
  337.                             try {
  338.                                 in.close();
  339.                             } catch (IOException e) {
  340.                                 // ignore
  341.                             }
  342.                         }
  343.                         if (out != null) {
  344.                             try {
  345.                                 out.close();
  346.                             } catch (IOException e) {
  347.                                 // ignore
  348.                             }
  349.                         }
  350.                     }
  351.                 }
  352.             } else {
  353.                 /*
  354.                  * For whatever reason we cannot write the
  355.                  * file to disk.
  356.                  */
  357.                 throw new FileUploadException(
  358.                     "Cannot write uploaded file to disk!");
  359.             }
  360.         }
  361.     }
  362.     /**
  363.      * Deletes the underlying storage for a file item, including deleting any
  364.      * associated temporary disk file. Although this storage will be deleted
  365.      * automatically when the <code>FileItem</code> instance is garbage
  366.      * collected, this method can be used to ensure that this is done at an
  367.      * earlier time, thus preserving system resources.
  368.      */
  369.     public void delete() {
  370.         cachedContent = null;
  371.         File outputFile = getStoreLocation();
  372.         if (outputFile != null && outputFile.exists()) {
  373.             outputFile.delete();
  374.         }
  375.     }
  376.     /**
  377.      * Returns the name of the field in the multipart form corresponding to
  378.      * this file item.
  379.      *
  380.      * @return The name of the form field.
  381.      *
  382.      * @see #setFieldName(java.lang.String)
  383.      *
  384.      */
  385.     public String getFieldName() {
  386.         return fieldName;
  387.     }
  388.     /**
  389.      * Sets the field name used to reference this file item.
  390.      *
  391.      * @param fieldName The name of the form field.
  392.      *
  393.      * @see #getFieldName()
  394.      *
  395.      */
  396.     public void setFieldName(String fieldName) {
  397.         this.fieldName = fieldName;
  398.     }
  399.     /**
  400.      * Determines whether or not a <code>FileItem</code> instance represents
  401.      * a simple form field.
  402.      *
  403.      * @return <code>true</code> if the instance represents a simple form
  404.      *         field; <code>false</code> if it represents an uploaded file.
  405.      *
  406.      * @see #setFormField(boolean)
  407.      *
  408.      */
  409.     public boolean isFormField() {
  410.         return isFormField;
  411.     }
  412.     /**
  413.      * Specifies whether or not a <code>FileItem</code> instance represents
  414.      * a simple form field.
  415.      *
  416.      * @param state <code>true</code> if the instance represents a simple form
  417.      *              field; <code>false</code> if it represents an uploaded file.
  418.      *
  419.      * @see #isFormField()
  420.      *
  421.      */
  422.     public void setFormField(boolean state) {
  423.         isFormField = state;
  424.     }
  425.     /**
  426.      * Returns an {@link java.io.OutputStream OutputStream} that can
  427.      * be used for storing the contents of the file.
  428.      *
  429.      * @return An {@link java.io.OutputStream OutputStream} that can be used
  430.      *         for storing the contensts of the file.
  431.      *
  432.      * @exception IOException if an error occurs.
  433.      */
  434.     public OutputStream getOutputStream()
  435.         throws IOException {
  436.         if (dfos == null) {
  437.             File outputFile = getTempFile();
  438.             dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
  439.         }
  440.         return dfos;
  441.     }
  442.     // --------------------------------------------------------- Public methods
  443.     /**
  444.      * Returns the {@link java.io.File} object for the <code>FileItem</code>'s
  445.      * data's temporary location on the disk. Note that for
  446.      * <code>FileItem</code>s that have their data stored in memory,
  447.      * this method will return <code>null</code>. When handling large
  448.      * files, you can use {@link java.io.File#renameTo(java.io.File)} to
  449.      * move the file to new location without copying the data, if the
  450.      * source and destination locations reside within the same logical
  451.      * volume.
  452.      *
  453.      * @return The data file, or <code>null</code> if the data is stored in
  454.      *         memory.
  455.      */
  456.     public File getStoreLocation() {
  457.         return dfos.getFile();
  458.     }
  459.     // ------------------------------------------------------ Protected methods
  460.     /**
  461.      * Removes the file contents from the temporary storage.
  462.      */
  463.     protected void finalize() {
  464.         File outputFile = dfos.getFile();
  465.         if (outputFile != null && outputFile.exists()) {
  466.             outputFile.delete();
  467.         }
  468.     }
  469.     /**
  470.      * Creates and returns a {@link java.io.File File} representing a uniquely
  471.      * named temporary file in the configured repository path. The lifetime of
  472.      * the file is tied to the lifetime of the <code>FileItem</code> instance;
  473.      * the file will be deleted when the instance is garbage collected.
  474.      *
  475.      * @return The {@link java.io.File File} to be used for temporary storage.
  476.      */
  477.     protected File getTempFile() {
  478.         File tempDir = repository;
  479.         if (tempDir == null) {
  480.             tempDir = new File(System.getProperty("java.io.tmpdir"));
  481.         }
  482.         String fileName = "upload_" + getUniqueId() + ".tmp";
  483.         File f = new File(tempDir, fileName);
  484.         FileCleaner.track(f, this);
  485.         return f;
  486.     }
  487.     // -------------------------------------------------------- Private methods
  488.     /**
  489.      * Returns an identifier that is unique within the class loader used to
  490.      * load this class, but does not have random-like apearance.
  491.      *
  492.      * @return A String with the non-random looking instance identifier.
  493.      */
  494.     private static String getUniqueId() {
  495.         int current;
  496.         synchronized (DiskFileItem.class) {
  497.             current = counter++;
  498.         }
  499.         String id = Integer.toString(current);
  500.         // If you manage to get more than 100 million of ids, you'll
  501.         // start getting ids longer than 8 characters.
  502.         if (current < 100000000) {
  503.             id = ("00000000" + id).substring(id.length());
  504.         }
  505.         return id;
  506.     }
  507.     
  508.     public String toString() {
  509.      return "name=" + this.getName()
  510. + ", StoreLocation=" 
  511. + String.valueOf(this.getStoreLocation())
  512. + ", size="
  513. + this.getSize()
  514. + "bytes, "
  515. + "isFormField=" + isFormField()
  516. + ", FieldName="
  517. + this.getFieldName();
  518.     }
  519. }