You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by so...@apache.org on 2011/11/10 19:10:56 UTC

svn commit: r1200459 - in /myfaces/trinidad/trunk: trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/

Author: sobryan
Date: Thu Nov 10 18:10:55 2011
New Revision: 1200459

URL: http://svn.apache.org/viewvc?rev=1200459&view=rev
Log:
TRINIDAD-2156

Thanks for the patch

Modified:
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java

Modified: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java?rev=1200459&r1=1200458&r2=1200459&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java (original)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/webapp/UploadedFileProcessor.java Thu Nov 10 18:10:55 2011
@@ -80,6 +80,15 @@ public interface UploadedFileProcessor
   public static final String TEMP_DIR_PARAM_NAME = "org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR";
 
   /**
+   * Initialization parameter for the default
+   * <code>UploadedFileProcessor</code> that configures the maximum
+   * file size that can be uploaded. The default is 2000 kilobytes.  Any requests that
+   * exceed this size will result in an EOFException being thrown
+   * on that request.
+   */
+  public static final String MAX_FILE_SIZE_PARAM_NAME = "org.apache.myfaces.trinidad.UPLOAD_MAX_FILE_SIZE";
+  
+  /**
    * Initialize the UploadedFileProcessor with access to the current
    * web application context. 
    * 

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java?rev=1200459&r1=1200458&r2=1200459&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/CompositeUploadedFileProcessorImpl.java Thu Nov 10 18:10:55 2011
@@ -111,7 +111,7 @@ public class CompositeUploadedFileProces
         {
           _LOG.severe(ioe);
           String test = ioe.getLocalizedMessage();
-          original = new ErrorFile(ioe.getLocalizedMessage());
+          original = new ErrorFile(tempFile.getFilename(), ioe.getLocalizedMessage());
           // The chain breaks if one of the chained processor throws an IOException, if the intent
           //  is to allow rest of the processors in chain to process, they could return custom 
           //  UploadedFile instance with length -1 and opaqueData having the failure details.
@@ -271,6 +271,26 @@ public class CompositeUploadedFileProces
           _tempDir = tempDirFile.getAbsolutePath();
       }
     }
+    
+    if (_maxFileSize == -1)
+    {
+      String maxFileSize = info.initParams.get(MAX_FILE_SIZE_PARAM_NAME);
+      if (maxFileSize != null)
+      {
+        try
+        {
+          _maxFileSize = Long.parseLong(maxFileSize);
+        }
+        catch (NumberFormatException nfe)
+        {
+          _maxFileSize = _DEFAULT_MAX_FILE_SIZE;
+        }
+      }
+      else
+      {
+        _maxFileSize = _DEFAULT_MAX_FILE_SIZE;
+      }
+    }
   }
 
   private UploadedFile _processFile(
@@ -288,7 +308,8 @@ public class CompositeUploadedFileProces
     Long maxMemory = (Long)requestMap.get(MAX_MEMORY_PARAM_NAME);
     Long maxDiskSpace = (Long)requestMap.get(MAX_DISK_SPACE_PARAM_NAME);
     String tempDir = (String)requestMap.get(TEMP_DIR_PARAM_NAME);
-
+    Long maxFileSizeSpace = (Long)requestMap.get(MAX_FILE_SIZE_PARAM_NAME);
+    
     if (maxMemory != null)
     {
       _maxMemory = maxMemory;
@@ -302,9 +323,19 @@ public class CompositeUploadedFileProces
     if (tempDir != null)
       _tempDir = tempDir;
 
+    if (maxFileSizeSpace != null)
+    {
+      _maxFileSize = maxFileSizeSpace;
+    }
+    
     if(contentLength>_maxDiskSpace)
     {
-      return new ErrorFile(_LOG.getMessage("UPLOADED_FILE_LARGE"));
+      return new ErrorFile(tempFile.getFilename(), _LOG.getMessage("UPLOADED_FILE_LARGE"));
+    }
+    // If the file is too large throw error
+    if(contentLength>_maxFileSize)
+    {
+      return new ErrorFile(tempFile.getFilename(), _LOG.getMessage("UPLOADED_FILE_LARGE"));
     }
     // Process one new file, loading only as much as can fit
     // in the remaining memory and disk space.
@@ -319,7 +350,7 @@ public class CompositeUploadedFileProces
     catch(IOException ioe)
     {
       _LOG.severe(ioe);
-      return new ErrorFile(ioe.getLocalizedMessage());
+      return new ErrorFile(tempFile.getFilename(), ioe.getLocalizedMessage());
     }
 
     // Keep a tally of how much we've stored in memory and on disk.
@@ -449,10 +480,12 @@ public class CompositeUploadedFileProces
 
   private long   _maxMemory = -1;
   private long   _maxDiskSpace = -1;
+  private long   _maxFileSize = -1;
   private String _tempDir = null;
 
   private static final long _DEFAULT_MAX_MEMORY = 102400;
   private static final long _DEFAULT_MAX_DISK_SPACE = 2048000;
+  private static final long _DEFAULT_MAX_FILE_SIZE = 2048000;
 
   private static final String _REQUEST_INFO_KEY = CompositeUploadedFileProcessorImpl.class.getName()+
     ".UploadedFilesInfo";

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java?rev=1200459&r1=1200458&r2=1200459&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/ErrorFile.java Thu Nov 10 18:10:55 2011
@@ -32,6 +32,12 @@ public class ErrorFile implements Upload
     _errorMessage = errorMessage;
   }
 
+  public ErrorFile(String filename, String errorMessage)
+  {
+    _filename = filename;
+    _errorMessage = errorMessage;
+  }
+  
   public void dispose()
   {
   }
@@ -43,7 +49,7 @@ public class ErrorFile implements Upload
 
   public String getFilename()
   {
-    return null;
+    return _filename;
   }
 
   public InputStream getInputStream() throws IOException
@@ -62,6 +68,7 @@ public class ErrorFile implements Upload
   }
   
   private String _errorMessage = null;
+  private String _filename = null;
 
   private static final long serialVersionUID = 2L;
 }
\ No newline at end of file

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java?rev=1200459&r1=1200458&r2=1200459&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/FileUploadConfiguratorImpl.java Thu Nov 10 18:10:55 2011
@@ -25,6 +25,8 @@ import java.io.InputStream;
 import java.lang.reflect.Proxy;
 
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 import javax.faces.context.ExternalContext;
@@ -33,6 +35,8 @@ import javax.portlet.faces.annotation.Ex
 
 import javax.servlet.http.HttpServletRequest;
 
+import javax.servlet.http.HttpServletResponse;
+
 import org.apache.myfaces.trinidad.config.Configurator;
 import org.apache.myfaces.trinidad.context.RequestContext;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
@@ -121,7 +125,7 @@ public class FileUploadConfiguratorImpl 
 
         final HashMap<String, String[]> parameters = new HashMap<String, String[]>();
         MultipartFormItem item;
-        final UploadedFiles files = new UploadedFiles(externalContext);
+        UploadedFiles files = new UploadedFiles(externalContext);
         while ((item = mfh.getNextPart()) != null)
         {
           final String name = item.getName();
@@ -144,12 +148,52 @@ public class FileUploadConfiguratorImpl 
               parameters.put(name, newArray);
             }
           }
-          // Upload a file
           else if (item.getFilename().length() > 0)
           {
+            // Upload a file
             _doUploadFile(RequestContext.getCurrentInstance(), externalContext, files, item);
           }
         }
+        if (parameters.containsKey(_MULTIPLE_UPLOAD_PARAM))
+        {
+          String uploadType = parameters.get(_MULTIPLE_UPLOAD_PARAM)[0];
+          if (uploadType != null)
+          {
+            UploadedFiles sessionFiles = UploadedFiles.getSessionUploadedFiles(externalContext);
+            if (uploadType.equals("multipleAdd"))
+            {
+              Map<String, List<UploadedFile>> uploadedMapFile = files.getUploadedFileMap();
+              Iterator iterator = uploadedMapFile.keySet().iterator();
+              while (iterator.hasNext())
+              {
+                String name = (String) iterator.next();
+                List<UploadedFile> fileList = uploadedMapFile.get(name);
+                for (UploadedFile file: fileList)
+                {
+                  sessionFiles.__put(name, file);
+                }
+              }
+              uploadedMapFile.clear();
+            }
+            else if (uploadType.equals("multipleDelete"))
+            {
+              String itemName = parameters.get("itemName")[0];
+              String fileName = parameters.get("fileName")[0];
+              List<UploadedFile> uploadedFiles = sessionFiles.getUploadedFileList(itemName);
+              if (uploadedFiles != null)
+              {
+                for (UploadedFile uploadedFile: uploadedFiles)
+                {
+                  if (uploadedFile.getFilename().equals(fileName))
+                  {
+                    uploadedFiles.remove(uploadedFile);
+                    break;
+                  }
+                }
+              }
+            }
+          }
+        }
         externalContext.getRequestMap().put(_PARAMS, parameters);
       }
       catch (Throwable t)
@@ -217,7 +261,8 @@ public class FileUploadConfiguratorImpl 
     _copyParamsFromSessionToRequestMap(sessionMap, requestMap,
       UploadedFileProcessor.MAX_MEMORY_PARAM_NAME,
       UploadedFileProcessor.MAX_DISK_SPACE_PARAM_NAME,
-      UploadedFileProcessor.TEMP_DIR_PARAM_NAME);
+      UploadedFileProcessor.TEMP_DIR_PARAM_NAME,
+      UploadedFileProcessor.MAX_FILE_SIZE_PARAM_NAME);
     
     final UploadedFile file =
       context.getUploadedFileProcessor().processFile(externalContext.getRequest(), temp);
@@ -363,6 +408,7 @@ public class FileUploadConfiguratorImpl 
   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(FileUploadConfiguratorImpl.class);
   static private final String _PARAMS = FileUploadConfiguratorImpl.class.getName()+".PARAMS";
   static private final boolean _ENHANCED_PORTLET_SUPPORTED = ExternalContextUtils.isRequestTypeSupported(RequestType.RESOURCE);
+  static private final String _MULTIPLE_UPLOAD_PARAM = "org.apache.myfaces.trinidad.UploadedFiles";
   
   private long _maxAllowedBytes = 1L << 27;
 }

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java?rev=1200459&r1=1200458&r2=1200459&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFileProcessorImpl.java Thu Nov 10 18:10:55 2011
@@ -120,6 +120,26 @@ public class UploadedFileProcessorImpl i
           _tempDir = tempDirFile.getAbsolutePath();
       }
     }
+    
+    if (_maxFileSize == -1)
+    {
+      String maxFileSize = info.initParams.get(MAX_FILE_SIZE_PARAM_NAME);
+      if (maxFileSize != null)
+      {
+        try
+        {
+          _maxFileSize = Long.parseLong(maxFileSize);
+        }
+        catch (NumberFormatException nfe)
+        {
+          _maxFileSize = _DEFAULT_MAX_FILE_SIZE;
+        }
+      }
+      else
+      {
+        _maxFileSize = _DEFAULT_MAX_FILE_SIZE;
+      }
+    }
   }
 
   public UploadedFile processFile(
@@ -153,7 +173,12 @@ public class UploadedFileProcessorImpl i
     
     if(contentLength>_maxDiskSpace)
     {
-      return new ErrorFile(_LOG.getMessage("UPLOADED_FILE_LARGE"));
+      return new ErrorFile(tempFile.getFilename(), _LOG.getMessage("UPLOADED_FILE_LARGE"));
+    }
+    // If the file is too large throw error
+    if(contentLength>_maxFileSize)
+    {
+      return new ErrorFile(tempFile.getFilename(), _LOG.getMessage("UPLOADED_FILE_LARGE"));
     }
     // Process one new file, loading only as much as can fit
     // in the remaining memory and disk space.
@@ -168,7 +193,7 @@ public class UploadedFileProcessorImpl i
     catch(IOException ioe)
     {
       _LOG.severe(ioe);
-      return new ErrorFile(ioe.getLocalizedMessage());
+      return new ErrorFile(tempFile.getFilename(), ioe.getLocalizedMessage());
     }
 
     // Keep a tally of how much we've stored in memory and on disk.
@@ -298,10 +323,12 @@ public class UploadedFileProcessorImpl i
 
   private long   _maxMemory = -1;
   private long   _maxDiskSpace = -1;
+  private long   _maxFileSize = -1;
   private String _tempDir = null;
 
   private static final long _DEFAULT_MAX_MEMORY = 102400;
   private static final long _DEFAULT_MAX_DISK_SPACE = 2048000;
+  private static final long _DEFAULT_MAX_FILE_SIZE = 2048000;
 
   private static final String _REQUEST_INFO_KEY = UploadedFileProcessorImpl.class.getName()+
     ".UploadedFilesInfo";

Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java?rev=1200459&r1=1200458&r2=1200459&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/config/upload/UploadedFiles.java Thu Nov 10 18:10:55 2011
@@ -18,24 +18,28 @@
  */
 package org.apache.myfaces.trinidadinternal.config.upload;
 
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
 import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
+
 import javax.portlet.PortletRequest;
+
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.myfaces.trinidad.model.UploadedFile;
-
 import org.apache.myfaces.trinidadinternal.share.util.CaboHttpUtils;
 
+
 /**
  * UploadedFiles defines the set of files that have been uploaded
  * to the server.
@@ -52,17 +56,41 @@ final public class UploadedFiles
   {
     return getUploadedFiles(context.getExternalContext());
   }
-
+  
   /**
    * Returns the map of uploaded files for the current request.
    */
   @SuppressWarnings("unchecked")
   static public UploadedFiles getUploadedFiles(ExternalContext context)
   {
-    Map<String, Object> requestMap =
-      context.getRequestMap();
+    Map<String, Object> requestMap = context.getRequestMap();
     return (UploadedFiles) requestMap.get(_UPLOADED_FILES_KEY);
   }
+  
+  /**
+   * Returns the map of uploaded files for the current request.
+   */
+  @SuppressWarnings("unchecked")
+  static public UploadedFiles getSessionUploadedFiles(FacesContext context)
+  {
+    return getSessionUploadedFiles(context.getExternalContext());
+  }
+  
+  /**
+   * Returns the map of uploaded files for the current session.
+   */
+  @SuppressWarnings("unchecked")
+  static public UploadedFiles getSessionUploadedFiles(ExternalContext context)
+  {
+    Map<String, Object> sessionMap = context.getSessionMap();
+    UploadedFiles files = (UploadedFiles) sessionMap.get(_UPLOADED_FILES_KEY);
+    if (files == null)
+    {
+      files = new UploadedFiles();
+      sessionMap.put(_UPLOADED_FILES_KEY, files);
+    }
+    return files;
+  }
 
   /**
    * Store the character encoding for the current request.
@@ -71,8 +99,7 @@ final public class UploadedFiles
     ExternalContext externalContext,
     String         encoding)
   {
-    UploadedFiles files = (UploadedFiles)
-      externalContext.getRequestMap().get(_UPLOADED_FILES_KEY);
+    UploadedFiles files = getUploadedFiles(externalContext);
     _setCharacterEncoding(files, encoding);
   }
 
@@ -93,7 +120,7 @@ final public class UploadedFiles
       req.getAttribute(_UPLOADED_FILES_KEY);
     _setCharacterEncoding(files, encoding);
   }
-
+  
   static private void _setCharacterEncoding(UploadedFiles files, String encoding)
   {
     if(files != null)
@@ -109,13 +136,63 @@ final public class UploadedFiles
    */
   public UploadedFile getUploadedFile(String name)
   {
-    UploadedFile file = _map.get(name);
-    if (file == null)
+    List<UploadedFile> files = getUploadedFileList(name);
+    if (files == null || files.isEmpty())
       return null;
 
-    return new FixFilename(file, _characterEncoding);
+    for (UploadedFile file : files)
+    {
+      if (file != null)
+      {
+        return file;
+      }
+    }
+    
+    return null;
   }
 
+  /**
+   * Returns a list of uploaded files.
+   * @param name the name under which the files are stored.  In HTML forms,
+   *   this will be derived from the "name" set on the &lt;input&gt; tag.
+   */
+  public List<UploadedFile> getUploadedFileList(String name)
+  {
+    List<UploadedFile> files = _map.get(name);
+    if (files == null || files.isEmpty())
+      return null;
+    
+    return files;
+  }
+
+  /**
+   * Retrieves files uploaded during a multiple file upload and copies those
+   * files to the requestMap so they get processed and cleaned up as usual
+   * @param name the name under which the files are stored.  In HTML forms,
+   *   this will be derived from the "name" set on the &lt;input&gt; tag.
+   */
+  public static void retrieveSessionUploadedFiles(ExternalContext context, String name)
+  {
+    UploadedFiles sessionFiles = (UploadedFiles) context.getSessionMap().get(_UPLOADED_FILES_KEY);
+    if (sessionFiles != null)
+    {
+      List<UploadedFile> sessionFileList = sessionFiles.getUploadedFileList(name);
+      if (sessionFileList != null && !sessionFileList.isEmpty())
+      {
+        UploadedFiles requestFiles = (UploadedFiles) context.getRequestMap().get(_UPLOADED_FILES_KEY);
+        if (requestFiles == null)
+        {
+          requestFiles = new UploadedFiles(context);
+        }
+        for (UploadedFile sessionFile: sessionFileList)
+        {
+          requestFiles.__put(name, sessionFile);
+        }
+        // clear it in the sessionMap
+        sessionFiles.getUploadedFileMap().remove(name);
+      }
+    }
+  }
 
   /**
    * Returns an Iterator of the names of all uploaded files.
@@ -124,6 +201,14 @@ final public class UploadedFiles
   {
     return _map.keySet().iterator();
   }
+  
+  /**
+   * Returns an Map of all of the uploaded files and names
+   */
+  public Map<String, List<UploadedFile>> getUploadedFileMap()
+  {
+    return _map;
+  }
 
   /**
    * Dispose of all UploadedFiles.  This will happen automatically
@@ -132,12 +217,21 @@ final public class UploadedFiles
    * processing files, this will free up resources earlier.
    */
   public void dispose()
-  {
-    Iterator<UploadedFile> iterator = _map.values().iterator();
+  {    
+    Iterator<List<UploadedFile>> iterator = _map.values().iterator();
     while (iterator.hasNext())
     {
-      UploadedFile file = iterator.next();
-      file.dispose();
+      List<UploadedFile> files = iterator.next();
+      if (files != null)
+      {
+        for (UploadedFile file: files)
+        {
+          if (file != null)
+          {
+            file.dispose();
+          }
+        }
+      }
     }
 
     _map.clear();
@@ -145,26 +239,39 @@ final public class UploadedFiles
     _totalMemory    = 0;
     _totalDiskSpace = 0;
   }
-
+  
   /**
    * Creates an UploadedFiles.
    */
   @SuppressWarnings("unchecked")
   UploadedFiles(ExternalContext externalContext)
   {
+    _map = new HashMap<String, List<UploadedFile>>();
     externalContext.getRequestMap().put(_UPLOADED_FILES_KEY, this);
-    _map = new HashMap<String, UploadedFile>();
   }
-
+  
+  /**
+   * Creates an UploadedFiles.
+   */
+  UploadedFiles()
+  {
+    _map = new HashMap<String, List<UploadedFile>>();
+  }
+  
   /**
    * Store a single UploadedFile.
    */
   void __put(String name, UploadedFile file)
   {
-    _map.put(name, file);
+    List<UploadedFile> files = _map.get(name);
+    if (files == null)
+    {
+      files = new ArrayList<UploadedFile>();
+    }
+    files.add(new FixFilename(file, _characterEncoding));
+    _map.put(name, files);
   }
 
-
   /**
    * Return the tally of total memory used.
    */
@@ -182,12 +289,10 @@ final public class UploadedFiles
     return _totalDiskSpace;
   }
 
-
-
   private long   _totalMemory;
   private long   _totalDiskSpace;
   private String _characterEncoding;
-  private final Map<String, UploadedFile> _map;
+  private final Map<String, List<UploadedFile>> _map;
 
   private static final String _UPLOADED_FILES_KEY =
     "org.apache.myfaces.trinidadinternal.webapp.UploadedFiles";