Home » apache-tomcat-6.0.26-src » org.apache » tomcat » util » http » fileupload » [javadoc | source]

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

Home » apache-tomcat-6.0.26-src » org.apache » tomcat » util » http » fileupload » [javadoc | source]