You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by ms...@apache.org on 2001/04/12 00:56:31 UTC

cvs commit: jakarta-struts/web/upload display.jsp upload.jsp

mschachter    01/04/11 15:56:30

  Modified:    src/share/org/apache/struts/upload
                        DiskMultipartRequestHandler.java
                        MultipartIterator.java
               src/upload/org/apache/struts/webapp/upload UploadAction.java
                        UploadForm.java
               web/upload display.jsp upload.jsp
  Added:       src/share/org/apache/struts/upload
                        BufferedMultipartInputStream.java
  Log:
  - Added file BufferedMultipartInputStream to
    handle buffering for ServletInputStream
    on it's own, and to provide a safer readLine() method
  - Added support for indexed values in multipart forms
  
  An interesting side to this, the time it takes for a file to upload
  seems to be dependant on the browser being used to upload.  For example
  there is a big difference between using Internet Explorer 5.5 and Opera 3.6, with
  Opera being about 7-10 times faster.
  
  Revision  Changes    Path
  1.8       +221 -208  jakarta-struts/src/share/org/apache/struts/upload/DiskMultipartRequestHandler.java
  
  Index: DiskMultipartRequestHandler.java
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/src/share/org/apache/struts/upload/DiskMultipartRequestHandler.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- DiskMultipartRequestHandler.java	2001/02/14 21:43:05	1.7
  +++ DiskMultipartRequestHandler.java	2001/04/11 22:56:19	1.8
  @@ -1,209 +1,222 @@
  -package org.apache.struts.upload;
  -
  -import java.io.File;
  -import java.io.IOException;
  -import java.io.FileOutputStream;
  -import java.io.ByteArrayInputStream;
  -import java.io.UnsupportedEncodingException;
  -import java.util.Hashtable;
  -import java.util.Enumeration;
  -import javax.servlet.ServletContext;
  -import javax.servlet.ServletException;
  -import javax.servlet.http.HttpServletRequest;
  -
  -import org.apache.struts.action.ActionServlet;
  -import org.apache.struts.action.ActionMapping;
  -
  -/**
  - * This is a MultipartRequestHandler that writes file data directly to
  - * to temporary files on disk
  - *
  - * @author Mike Schachter
  - */
  -public class DiskMultipartRequestHandler implements MultipartRequestHandler {
  -
  -    /**
  -     * The ActionServlet instance used for this class
  -     */
  -    protected ActionServlet servlet;
  -
  -    /**
  -     * The ActionMapping instance used for this class
  -     */
  -    protected ActionMapping mapping;
  -
  -    /**
  -     * A Hashtable representing the form files uploaded
  -     */
  -    protected Hashtable fileElements;
  -
  -    /**
  -     * A Hashtable representing the form text input names and values
  -     */
  -    protected Hashtable textElements;
  -
  -    /**
  -     * A Hashtable representing all elemnents
  -     */
  -    protected Hashtable allElements;
  -    
  -    /**
  -     * The temporary directory
  -     */
  -    protected String tempDir;
  -    
  -    public void handleRequest(HttpServletRequest request) throws ServletException {
  -        
  -        retrieveTempDir();
  -        
  -        MultipartIterator iterator = new MultipartIterator(request,
  -                                                            servlet.getBufferSize(),
  -                                                            getMaxSizeFromServlet(),
  -                                                            tempDir);
  -        MultipartElement element;
  -
  -        textElements = new Hashtable();
  -        fileElements = new Hashtable();
  -        allElements = new Hashtable();
  -
  -        try {
  -            while ((element = iterator.getNextElement()) != null) {
  -                if (!element.isFile()) {                    
  -                    textElements.put(element.getName(), element.getValue());
  -                    allElements.put(element.getName(), element.getValue());
  -                }
  -                else {
  -                    
  -                     File tempFile = element.getFile();
  -                     if (tempFile.exists()) {
  -                         DiskFile theFile = new DiskFile(tempFile.getAbsolutePath());
  -                         theFile.setContentType(element.getContentType());
  -                         theFile.setFileName(element.getFileName());
  -                         theFile.setFileSize((int) tempFile.length());
  -                         fileElements.put(element.getName(), theFile);
  -                         allElements.put(element.getName(), theFile);
  -                     }             
  -                }
  -            }
  -        }
  -        catch (UnsupportedEncodingException uee) {
  -            throw new ServletException("Encoding \"ISO-8859-1\" not supported");
  -        }
  -
  -    }
  -
  -    public Hashtable getAllElements() {
  -        return allElements;
  -    }
  -
  -    public Hashtable getTextElements() {
  -        return textElements;
  -    }
  -
  -    public Hashtable getFileElements() {
  -        return fileElements;
  -    }
  -
  -    /**
  -     * Delete all the files uploaded
  -     */
  -    public void rollback() {
  -        Enumeration names = fileElements.keys();
  -
  -        while (names.hasMoreElements()) {
  -            String name = (String) names.nextElement();
  -            DiskFile theFile = (DiskFile) fileElements.get(name);
  -            theFile.destroy();
  -        }
  -    }
  -
  -    /**
  -     * Calls on {@link #rollback() rollback()} to delete
  -     * temporary files
  -     */
  -    public void finish() {
  -        rollback();
  -    }
  -
  -    public void setServlet(ActionServlet servlet) {
  -        this.servlet = servlet;
  -    }
  -
  -    public void setMapping(ActionMapping mapping) {
  -        this.mapping = mapping;
  -    }
  -
  -    public ActionServlet getServlet() {
  -        return servlet;
  -    }
  -
  -    public ActionMapping getMapping() {
  -        return mapping;
  -    }
  -
  -    /**
  -     * Gets the maximum post data size in bytes from the string
  -     * representation in ActionServlet
  -     */
  -    protected long getMaxSizeFromServlet() throws ServletException{
  -        String stringSize = servlet.getMaxFileSize();
  -        long size = -1;
  -        int multiplier = 1;
  -        
  -        if (stringSize.endsWith("K")) {
  -            multiplier = 1024;
  -            stringSize = stringSize.substring(0, stringSize.length()-1);
  -        }
  -        if (stringSize.endsWith("M")) {
  -            multiplier = 1024*1024;
  -            stringSize = stringSize.substring(0, stringSize.length()-1);
  -        }
  -        else if (stringSize.endsWith("G")) {
  -            multiplier = 1024*1024*1024;
  -            stringSize = stringSize.substring(0, stringSize.length()-1);
  -        }
  -        
  -        try {
  -            size = Long.parseLong(stringSize);
  -        }
  -        catch (NumberFormatException nfe) {
  -            throw new ServletException("Invalid format for maximum file size: \"" +
  -                servlet.getMaxFileSize() + "\"");
  -        }
  -                
  -        return (size * multiplier);
  -    }       
  -    
  -    /**
  -     * Retrieves the temporary directory from either ActionServlet, a context
  -     * property, or a system property, in that order
  -     */
  -    protected void retrieveTempDir() { 
  -        //get a handle to some temporary file and open
  -        //a stream to it
  -        tempDir = servlet.getTempDir();
  -        if (tempDir == null) {
  -            //attempt to retrieve the servlet container's temporary directory
  -            ServletContext context = servlet.getServletConfig().getServletContext();
  -
  -            try {
  -                tempDir = (String) context.getAttribute("javax.servlet.context.tempdir");
  -            }
  -            catch (ClassCastException cce) {
  -                tempDir = ((File) context.getAttribute("javax.servlet.context.tempdir")).getAbsolutePath();
  -            }
  -            
  -
  -            if (tempDir == null) {
  -                //default to system-wide tempdir
  -                tempDir = System.getProperty("java.io.tmpdir");
  -
  -                if (servlet.getDebug() > 1) {
  -                    servlet.log("DiskMultipartRequestHandler.handleRequest(): " +
  -                    "defaulting to java.io.tmpdir directory \"" +
  -                    tempDir);
  -                }
  -            }
  -        }
  -    }
  +package org.apache.struts.upload;
  +
  +import java.io.File;
  +import java.io.IOException;
  +import java.io.FileOutputStream;
  +import java.io.ByteArrayInputStream;
  +import java.io.UnsupportedEncodingException;
  +import java.util.Hashtable;
  +import java.util.Enumeration;
  +import javax.servlet.ServletContext;
  +import javax.servlet.ServletException;
  +import javax.servlet.http.HttpServletRequest;
  +
  +import org.apache.struts.action.ActionServlet;
  +import org.apache.struts.action.ActionMapping;
  +
  +/**
  + * This is a MultipartRequestHandler that writes file data directly to
  + * to temporary files on disk
  + *
  + * @author Mike Schachter
  + */
  +public class DiskMultipartRequestHandler implements MultipartRequestHandler {
  +
  +    /**
  +     * The ActionServlet instance used for this class
  +     */
  +    protected ActionServlet servlet;
  +
  +    /**
  +     * The ActionMapping instance used for this class
  +     */
  +    protected ActionMapping mapping;
  +
  +    /**
  +     * A Hashtable representing the form files uploaded
  +     */
  +    protected Hashtable fileElements;
  +
  +    /**
  +     * A Hashtable representing the form text input names and values
  +     */
  +    protected Hashtable textElements;
  +
  +    /**
  +     * A Hashtable representing all elemnents
  +     */
  +    protected Hashtable allElements;
  +    
  +    /**
  +     * The temporary directory
  +     */
  +    protected String tempDir;
  +    
  +    public void handleRequest(HttpServletRequest request) throws ServletException {
  +        
  +        retrieveTempDir();
  +        
  +        MultipartIterator iterator = new MultipartIterator(request,
  +                                                            servlet.getBufferSize(),
  +                                                            getMaxSizeFromServlet(),
  +                                                            tempDir);
  +        MultipartElement element;
  +
  +        textElements = new Hashtable();
  +        fileElements = new Hashtable();
  +        allElements = new Hashtable();
  +
  +        try {
  +            while ((element = iterator.getNextElement()) != null) {
  +                if (!element.isFile()) {
  +                    
  +                    String[] textValues = (String[]) textElements.get(element.getName());
  +                    if (textValues != null) {                        
  +                        String[] textValues2 = new String[textValues.length + 1];
  +                        System.arraycopy(textValues, 0, textValues2, 0, textValues.length);
  +                        textValues2[textValues.length] = element.getValue();
  +                        textValues = textValues2;
  +                    }
  +                    else {
  +                        textValues = new String[1];
  +                        textValues[0] = element.getValue();
  +                    }
  +                    
  +                    textElements.put(element.getName(), textValues);
  +                    allElements.put(element.getName(), textValues);
  +                }
  +                else {
  +                    
  +                     File tempFile = element.getFile();
  +                     if (tempFile.exists()) {
  +                         DiskFile theFile = new DiskFile(tempFile.getAbsolutePath());
  +                         theFile.setContentType(element.getContentType());
  +                         theFile.setFileName(element.getFileName());
  +                         theFile.setFileSize((int) tempFile.length());
  +                         fileElements.put(element.getName(), theFile);
  +                         allElements.put(element.getName(), theFile);
  +                     }             
  +                }
  +            }
  +        }
  +        catch (UnsupportedEncodingException uee) {
  +            throw new ServletException("Encoding \"ISO-8859-1\" not supported");
  +        }
  +
  +    }
  +
  +    public Hashtable getAllElements() {
  +        return allElements;
  +    }
  +
  +    public Hashtable getTextElements() {
  +        return textElements;
  +    }
  +
  +    public Hashtable getFileElements() {
  +        return fileElements;
  +    }
  +
  +    /**
  +     * Delete all the files uploaded
  +     */
  +    public void rollback() {
  +        Enumeration names = fileElements.keys();
  +
  +        while (names.hasMoreElements()) {
  +            String name = (String) names.nextElement();
  +            DiskFile theFile = (DiskFile) fileElements.get(name);
  +            theFile.destroy();
  +        }
  +    }
  +
  +    /**
  +     * Calls on {@link #rollback() rollback()} to delete
  +     * temporary files
  +     */
  +    public void finish() {
  +        rollback();
  +    }
  +
  +    public void setServlet(ActionServlet servlet) {
  +        this.servlet = servlet;
  +    }
  +
  +    public void setMapping(ActionMapping mapping) {
  +        this.mapping = mapping;
  +    }
  +
  +    public ActionServlet getServlet() {
  +        return servlet;
  +    }
  +
  +    public ActionMapping getMapping() {
  +        return mapping;
  +    }
  +
  +    /**
  +     * Gets the maximum post data size in bytes from the string
  +     * representation in ActionServlet
  +     */
  +    protected long getMaxSizeFromServlet() throws ServletException{
  +        String stringSize = servlet.getMaxFileSize();
  +        long size = -1;
  +        int multiplier = 1;
  +        
  +        if (stringSize.endsWith("K")) {
  +            multiplier = 1024;
  +            stringSize = stringSize.substring(0, stringSize.length()-1);
  +        }
  +        if (stringSize.endsWith("M")) {
  +            multiplier = 1024*1024;
  +            stringSize = stringSize.substring(0, stringSize.length()-1);
  +        }
  +        else if (stringSize.endsWith("G")) {
  +            multiplier = 1024*1024*1024;
  +            stringSize = stringSize.substring(0, stringSize.length()-1);
  +        }
  +        
  +        try {
  +            size = Long.parseLong(stringSize);
  +        }
  +        catch (NumberFormatException nfe) {
  +            throw new ServletException("Invalid format for maximum file size: \"" +
  +                servlet.getMaxFileSize() + "\"");
  +        }
  +                
  +        return (size * multiplier);
  +    }       
  +    
  +    /**
  +     * Retrieves the temporary directory from either ActionServlet, a context
  +     * property, or a system property, in that order
  +     */
  +    protected void retrieveTempDir() { 
  +        //get a handle to some temporary file and open
  +        //a stream to it
  +        tempDir = servlet.getTempDir();
  +        if (tempDir == null) {
  +            //attempt to retrieve the servlet container's temporary directory
  +            ServletContext context = servlet.getServletConfig().getServletContext();
  +
  +            try {
  +                tempDir = (String) context.getAttribute("javax.servlet.context.tempdir");
  +            }
  +            catch (ClassCastException cce) {
  +                tempDir = ((File) context.getAttribute("javax.servlet.context.tempdir")).getAbsolutePath();
  +            }
  +            
  +
  +            if (tempDir == null) {
  +                //default to system-wide tempdir
  +                tempDir = System.getProperty("java.io.tmpdir");
  +
  +                if (servlet.getDebug() > 1) {
  +                    servlet.log("DiskMultipartRequestHandler.handleRequest(): " +
  +                    "defaulting to java.io.tmpdir directory \"" +
  +                    tempDir);
  +                }
  +            }
  +        }
  +    }
   }
  
  
  
  1.10      +547 -498  jakarta-struts/src/share/org/apache/struts/upload/MultipartIterator.java
  
  Index: MultipartIterator.java
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/src/share/org/apache/struts/upload/MultipartIterator.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- MultipartIterator.java	2001/02/14 21:43:06	1.9
  +++ MultipartIterator.java	2001/04/11 22:56:20	1.10
  @@ -1,498 +1,547 @@
  -package org.apache.struts.upload;
  -
  -import java.io.File;
  -import java.io.InputStream;
  -import java.io.IOException;
  -import java.io.OutputStream;
  -import java.io.FileOutputStream;
  -import java.io.UnsupportedEncodingException;
  -import javax.servlet.ServletException;
  -import javax.servlet.ServletInputStream;
  -import javax.servlet.http.HttpServletRequest;
  -
  -/**
  - * The MultipartIterator class is responsible for reading the
  - * input data of a multipart request and splitting it up into
  - * input elements, wrapped inside of a
  - * {@link org.apache.struts.upload.MultipartElement MultipartElement}
  - * for easy definition.  To use this class, create a new instance
  - * of MultipartIterator passing it a HttpServletRequest in the
  - * constructor.  Then use the {@link #getNextElement() getNextElement}
  - * method until it returns null, then you're finished.  Example: <br>
  - * <pre>
  - *      MultipartIterator iterator = new MultipartIterator(request);
  - *      MultipartElement element;
  - * 
  - *      while ((element = iterator.getNextElement()) != null) {
  - *           //do something with element
  - *      }
  - * </pre>
  - *
  - * @see org.apache.struts.upload.MultipartElement
  - * @author Mike Schachter
  - */
  -public class MultipartIterator {
  -    
  -    /**
  -     * The request instance for this class
  -     */
  -    protected HttpServletRequest request;
  -    
  -    /**
  -     * The input stream instance for this class
  -     */
  -    protected ServletInputStream inputStream;
  -    
  -    /**
  -     * The boundary for this multipart request
  -     */
  -    protected String boundary;
  -    
  -    /**
  -     * Whether or not the input stream is finished
  -     */
  -    protected boolean contentRead = false;
  -    
  -    /**
  -     * The maximum file size in bytes allowed. Ignored if -1
  -     */
  -    protected long maxSize = -1;
  -    
  -    /**
  -     * The total bytes read from this request
  -     */
  -    protected long totalLength = 0;
  -    
  -    /**
  -     * The content length of this request
  -     */
  -    protected int contentLength;
  -    
  -    /**
  -     * The amount of data read from a request at a time.
  -     * This also represents the maximum size in bytes of
  -     * a line read from the request
  -     * Defaults to 4 * 1024 (4 KB)
  -     */
  -    protected int bufferSize = 4 * 1024;
  -    
  -    /**
  -     * The temporary directory to store files
  -     */
  -    protected String tempDir;
  -
  -    /**
  -     * Constructs a MultipartIterator with a default buffer size and no file size
  -     * limit
  -     * 
  -     * @param request The multipart request to iterate
  -     */
  -    public MultipartIterator(HttpServletRequest request) throws ServletException{
  -        this(request, -1);
  -    }
  -    
  -    /**
  -     * Constructs a MultipartIterator with the specified buffer size and
  -     * no file size limit
  -     *
  -     * @param request The multipart request to iterate
  -     * @param bufferSize The size in bytes that should be read from the input
  -     *                   stream at a times
  -     */
  -    public MultipartIterator(HttpServletRequest request, int bufferSize) throws ServletException {        
  -       this (request, bufferSize, -1);
  -    }
  -    
  -    /**
  -     * Constructs a MultipartIterator with the specified buffer size and
  -     * the specified file size limit in bytes
  -     *
  -     * @param request The multipart request to iterate
  -     * @param bufferSize The size in bytes that should be read from the input
  -     *                   stream at a times
  -     * @param maxSize The maximum size in bytes allowed for a multipart element's data
  -     */
  -    public MultipartIterator(HttpServletRequest request, int bufferSize, long maxSize) 
  -                                                                 throws ServletException {
  -                         
  -        this(request, bufferSize, maxSize, null);                                                                 
  -        
  -    }
  -    
  -    public MultipartIterator(HttpServletRequest request,
  -                             int bufferSize,
  -                             long maxSize,
  -                             String tempDir) throws ServletException {
  -                                 
  -        this.request = request;
  -        this.maxSize = maxSize;
  -        if (bufferSize > -1) {
  -            this.bufferSize = bufferSize;
  -        }
  -        if (tempDir != null) {
  -            this.tempDir = tempDir;
  -        }
  -        else {
  -            //default to system-wide tempdir
  -            tempDir = System.getProperty("java.io.tmpdir");
  -        }
  -        parseRequest();
  -    }
  -    
  -    /**
  -     * Retrieves the next element in the iterator if one exists.
  -     *
  -     * @throws a ServletException if the post size exceeds the maximum file size
  -     *         passed in the 3 argument constructor
  -     * @throws an UnsupportedEncodingException if the "ISO-8859-1" encoding isn't found
  -     * @return a {@link org.apache.struts.upload.MultipartElement MultipartElement}
  -     *         representing the next element in the request data
  -     *
  -     */
  -    public MultipartElement getNextElement() throws ServletException, UnsupportedEncodingException {
  -        //retrieve the "Content-Disposition" header
  -        //and parse
  -        String disposition = readLine();
  -        
  -        if ((disposition != null) && (disposition.startsWith("Content-Disposition"))) {
  -            String name = parseDispositionName(disposition);
  -            String filename = parseDispositionFilename(disposition);
  -                                   
  -            String contentType = null;
  -            boolean isFile = (filename != null);
  -            
  -            if (isFile) {
  -                filename = new File(filename).getName();
  -                
  -                //check for windows filenames,
  -                //from linux jdk's the entire filepath
  -                //isn't parsed correctly from File.getName()
  -                int colonIndex = filename.indexOf(":");
  -                int slashIndex = filename.lastIndexOf("\\");
  -                
  -                if ((colonIndex > -1) && (slashIndex > -1)) {
  -                    //then consider this filename to be a full
  -                    //windows filepath, and parse it accordingly
  -                    //to retrieve just the file name
  -                    filename = filename.substring(slashIndex+1, filename.length());
  -                }
  -                
  -                
  -                
  -                //get the content type
  -                contentType = readLine();
  -                contentType = parseContentType(contentType);
  -            }
  -            
  -           
  -            
  -            //ignore next line (whitespace) (unless it's a file
  -            //without content-type)
  -	    if (! ((isFile) && contentType == null)) {
  -		readLine();
  -            }
  -            
  -            MultipartElement element = null;
  -            
  -            //process a file element
  -            if (isFile) {
  -                try {
  -                    //create a local file on disk representing the element
  -                    File elementFile = createLocalFile();
  -
  -                    element = new MultipartElement(name, filename, contentType, elementFile);
  -                } catch (IOException ioe) {
  -                    throw new ServletException("IOException while reading file element: ioe.getMessage()", ioe);
  -                }
  -            }
  -            else {
  -                 //read data into String form, then convert to bytes
  -                //for text
  -                StringBuffer textData = new StringBuffer();
  -                String line;
  -                //parse for text data
  -                line = readLine();
  -
  -                while ((line != null) && (!line.startsWith(boundary))) {
  -                    textData.append(line);
  -                    line = readLine();
  -
  -                    if (maxSize > -1) {
  -                        if (totalLength > maxSize) {
  -                            throw new ServletException("Multipart data size exceeds the maximum " +
  -                                "allowed post size");
  -                        }
  -                    }
  -                }
  -
  -                if (textData.length() > 1) {
  -                    //cut off "\r\n" from the end
  -                    textData.setLength(textData.length()-2);
  -                }
  -                
  -                //create the element
  -                element = new MultipartElement(name, textData.toString());
  -            }
  -            return element;
  -        }       
  -        //reset stream
  -        if (inputStream.markSupported()) {
  -            try {
  -                inputStream.reset();
  -            }
  -            catch (IOException ioe) {
  -                throw new ServletException("IOException while resetting input stream: " +
  -                    ioe.getMessage());
  -            }
  -        }
  -        return null;       
  -    }
  -    
  -    /**
  -     * Set the maximum amount of bytes read from a line at one time
  -     *
  -     * @see javax.servlet.ServletInputStream#readLine(byte[], int, int)
  -     */
  -    public void setBufferSize(int bufferSize) {
  -        this.bufferSize = bufferSize;
  -    }
  -    
  -    /**
  -     * Get the maximum amount of bytes read from a line at one time
  -     *
  -     * @see javax.servlet.ServletInputStream#readLine(byte[], int, int)
  -     */
  -    public int getBufferSize() {
  -        return bufferSize;
  -    }
  -    
  -    /**
  -     * Set the maximum post data size allowed for a multipart request
  -     * @param maxSize The maximum post data size in bytes, set to <code>-1</code>
  -     *                for no limit
  -     */
  -    public void setMaxSize(long maxSize) {
  -        this.maxSize = maxSize;
  -    }
  -    
  -    /** 
  -     * Get the maximum post data size allowed for a multipart request
  -     * @return The maximum post data size in bytes
  -     */
  -    public long getMaxSize() {
  -        return maxSize;
  -    }
  -    
  -    /**
  -     * Handles retrieving the boundary and setting the input stream
  -     */
  -    protected void parseRequest() throws ServletException {
  -             
  -        contentLength = request.getContentLength();
  -        
  -        //set boundary
  -        boundary = parseBoundary(request.getContentType());
  -        
  -        try {
  -            //set the input stream
  -            inputStream = request.getInputStream();
  -            //mark the input stream to allow multiple reads
  -            if (inputStream.markSupported()) {
  -                inputStream.mark(contentLength+1);
  -            }
  -                
  -        }
  -        catch (IOException ioe) {
  -            throw new ServletException("MultipartIterator.parseRequest(): " +
  -                                       "IOException while trying to obtain " +
  -                                       "ServletInputStream");
  -        }
  -       
  -        if ((boundary == null) || (boundary.length() < 1)) {
  -            //try retrieving the header through more "normal" means
  -            boundary = parseBoundary(request.getHeader("Content-type"));
  -        }
  -        
  -        if ((boundary == null) || (boundary.length() < 1)) {
  -            throw new ServletException("MultipartIterator: cannot retrieve boundary " +
  -                                       "for multipart request");
  -        }
  -        
  -        //read first line
  -        try {
  -	    String firstLine = readLine();
  -
  -	    if (firstLine == null) {
  -		throw new ServletException("MultipartIterator: no multipart request data " +
  -					   "sent");
  -	    }
  -            if (!firstLine.startsWith(boundary)) {
  -                throw new ServletException("MultipartIterator: invalid multipart request " +
  -                                           "data, doesn't start with boundary");
  -            }
  -        }
  -        catch (UnsupportedEncodingException uee) {
  -            throw new ServletException("MultipartIterator: encoding \"ISO-8859-1\" not supported");
  -        }
  -    }
  -      
  -    /**
  -     * Parses a content-type String for the boundary.  Appends a 
  -     * "--" to the beginning of the boundary, because thats the
  -     * real boundary as opposed to the shortened one in the
  -     * content type.
  -     */
  -    public static String parseBoundary(String contentType) {
  -        if (contentType.lastIndexOf("boundary=") != -1) {
  -            String _boundary = "--" + 
  -                               contentType.substring(contentType.lastIndexOf("boundary=")+9);
  -            if (_boundary.endsWith("\n")) {
  -                //strip it off
  -                return _boundary.substring(0, _boundary.length()-1);
  -            }
  -            return _boundary; 
  -        }
  -        return null;      
  -    }
  -    
  -    /**
  -     * Parses the "Content-Type" line of a multipart form for a content type
  -     *
  -     * @param contentTypeString A String reprsenting the Content-Type line, 
  -     *        with a trailing "\n"
  -     * @return The content type specified, or <code>null</code> if one can't be
  -     *         found.
  -     */
  -    public static String parseContentType(String contentTypeString) {
  -        int nameIndex = contentTypeString.indexOf("Content-Type: ");
  -        
  -        if (nameIndex != -1) {
  -            int endLineIndex = contentTypeString.indexOf("\n");
  -            if (endLineIndex != -1) {
  -                return contentTypeString.substring(nameIndex+14, endLineIndex);
  -            }
  -        }
  -        return null;
  -    }
  -    
  -    /**
  -     * Retrieves the "name" attribute from a content disposition line
  -     * 
  -     * @param dispositionString The entire "Content-disposition" string
  -     * @return <code>null</code> if no name could be found, otherwise,
  -     *         returns the name
  -     * @see #parseForAttribute(String, String)
  -     */
  -    public static String parseDispositionName(String dispositionString) {
  -        return parseForAttribute("name", dispositionString);
  -    }
  -    
  -    /** 
  -     * Retrieves the "filename" attribute from a content disposition line
  -     *
  -     * @param dispositionString The entire "Content-disposition" string
  -     * @return <code>null</code> if no filename could be found, otherwise,
  -     *         returns the filename
  -     * @see #parseForAttribute(String, String)
  -     */
  -    public static String parseDispositionFilename(String dispositionString) {
  -        return parseForAttribute("filename", dispositionString);
  -    }
  -        
  -    
  -    /**
  -     * Parses a string looking for a attribute-value pair, and returns the value.
  -     * For example:
  -     * <pre>
  -     *      String parseString = "Content-Disposition: filename=\"bob\" name=\"jack\"";
  -     *      MultipartIterator.parseForAttribute(parseString, "name");
  -     * </pre>
  -     * That will return "bob".
  -     * 
  -     * @param attribute The name of the attribute you're trying to get
  -     * @param parseString The string to retrieve the value from
  -     * @return The value of the attribute, or <code>null</code> if none could be found
  -     */
  -    public static String parseForAttribute(String attribute, String parseString) {
  -        int nameIndex = parseString.indexOf(attribute + "=\"");
  -        if (nameIndex != -1) {
  -            
  -            int endQuoteIndex = parseString.indexOf("\"", nameIndex+attribute.length()+3);
  -            
  -            if (endQuoteIndex != -1) {
  -                return parseString.substring(nameIndex+attribute.length()+2, endQuoteIndex);
  -            }
  -            return "";
  -        }        
  -        return null;
  -    }
  -    
  -    /**
  -     * Reads the input stream until it reaches a new line
  -     */
  -    protected String readLine() throws ServletException, UnsupportedEncodingException {
  -       
  -        byte[] bufferByte = new byte[bufferSize];
  -        int bytesRead;
  -        
  -        if (totalLength >= contentLength) {
  -            return null;
  -        }
  -        
  -        try {
  -            bytesRead = inputStream.readLine(bufferByte,
  -                                             0,
  -                                             bufferSize);
  -        }
  -        catch (IOException ioe) {
  -            throw new ServletException("IOException while reading multipart request: " + 
  -				       ioe.getMessage());
  -        }
  -        if (bytesRead == -1) {
  -            return null;
  -        }
  -        
  -        totalLength += bytesRead;
  -        return new String(bufferByte, 0, bytesRead, "ISO-8859-1");
  -    }
  -    
  -    /**
  -     * Creates a file on disk from the current mulitpart element
  -     * @param fileName the name of the multipart file
  -     */
  -    protected File createLocalFile() throws IOException,ServletException {
  -        
  -        File tempFile = File.createTempFile("strts", null, new File(tempDir));
  -        OutputStream fos = new FileOutputStream(tempFile);
  -        
  -	byte[] bufferBytes = new byte[bufferSize];
  -	InputStream requestIn = new MultipartValueStream(inputStream, boundary);
  -	
  -	int bytesRead = 0;
  -	while (bytesRead != -1) {
  -            
  -	    bytesRead = requestIn.read(bufferBytes);
  -            
  -            if (bytesRead > 0) {
  -                totalLength += bytesRead;
  -            }
  -            
  -            //check to make sure the read doesn't exceed the bounds
  -	    if (bytesRead > 0) {
  -                if (maxSize > -1) {
  -                    if (totalLength > maxSize) {
  -                        throw new ServletException("Multipart data size exceeds the maximum " +
  -                            "allowed post size");
  -                    }
  -                }
  -            }
  -            if (bytesRead > 0) {
  -                fos.write(bufferBytes, 0, bytesRead);
  -            }
  -	}
  -        	
  -        fos.close();
  -        return tempFile;
  -    }
  -
  -}
  +package org.apache.struts.upload;
  +
  +import java.io.File;
  +import java.io.IOException;
  +import java.io.BufferedOutputStream;
  +import java.io.FileOutputStream;
  +import java.io.UnsupportedEncodingException;
  +import javax.servlet.ServletException;
  +import javax.servlet.ServletInputStream;
  +import javax.servlet.http.HttpServletRequest;
  +
  +/**
  + * The MultipartIterator class is responsible for reading the
  + * input data of a multipart request and splitting it up into
  + * input elements, wrapped inside of a
  + * {@link org.apache.struts.upload.MultipartElement MultipartElement}
  + * for easy definition.  To use this class, create a new instance
  + * of MultipartIterator passing it a HttpServletRequest in the
  + * constructor.  Then use the {@link #getNextElement() getNextElement}
  + * method until it returns null, then you're finished.  Example: <br>
  + * <pre>
  + *      MultipartIterator iterator = new MultipartIterator(request);
  + *      MultipartElement element;
  + * 
  + *      while ((element = iterator.getNextElement()) != null) {
  + *           //do something with element
  + *      }
  + * </pre>
  + *
  + * @see org.apache.struts.upload.MultipartElement
  + * @author Mike Schachter
  + */
  +public class MultipartIterator {
  +    
  +    /**
  +     * The maximum size in bytes of the buffer used to read lines [4K]
  +     */
  +    public static int MAX_LINE_SIZE = 4096;
  +    
  +    /**
  +     * The request instance for this class
  +     */
  +    protected HttpServletRequest request;
  +    
  +    /**
  +     * The input stream instance for this class
  +     */
  +    protected BufferedMultipartInputStream inputStream;
  +    
  +    /**
  +     * The boundary for this multipart request
  +     */
  +    protected String boundary;
  +    
  +    /**
  +     * The byte array representing the boundary for this multipart request
  +     */
  +    protected byte[] boundaryBytes;
  +    
  +    /**
  +     * Whether or not the input stream is finished
  +     */
  +    protected boolean contentRead = false;
  +    
  +    /**
  +     * The maximum file size in bytes allowed. Ignored if -1
  +     */
  +    protected long maxSize = -1;
  +    
  +    /**
  +     * The total bytes read from this request
  +     */
  +    protected long totalLength = 0;
  +    
  +    /**
  +     * The content length of this request
  +     */
  +    protected int contentLength;
  +    
  +    /**
  +     * The size in bytes written to the filesystem at a time [20K]
  +     */
  +    protected int diskBufferSize = 2 * 10240;
  +    
  +    /**
  +     * The amount of data read from a request at a time.
  +     * This also represents the maximum size in bytes of
  +     * a line read from the request [4KB]
  +     */
  +    protected int bufferSize = 4096;
  +    
  +    /**
  +     * The temporary directory to store files
  +     */
  +    protected String tempDir;
  +
  +    /**
  +     * Constructs a MultipartIterator with a default buffer size and no file size
  +     * limit
  +     * 
  +     * @param request The multipart request to iterate
  +     */
  +    public MultipartIterator(HttpServletRequest request) throws ServletException{
  +        this(request, -1);
  +    }
  +    
  +    /**
  +     * Constructs a MultipartIterator with the specified buffer size and
  +     * no file size limit
  +     *
  +     * @param request The multipart request to iterate
  +     * @param bufferSize The size in bytes that should be read from the input
  +     *                   stream at a times
  +     */
  +    public MultipartIterator(HttpServletRequest request, int bufferSize) throws ServletException {        
  +       this (request, bufferSize, -1);
  +    }
  +    
  +    /**
  +     * Constructs a MultipartIterator with the specified buffer size and
  +     * the specified file size limit in bytes
  +     *
  +     * @param request The multipart request to iterate
  +     * @param bufferSize The size in bytes that should be read from the input
  +     *                   stream at a times
  +     * @param maxSize The maximum size in bytes allowed for a multipart element's data
  +     */
  +    public MultipartIterator(HttpServletRequest request, int bufferSize, long maxSize) 
  +                                                                 throws ServletException {
  +                         
  +        this(request, bufferSize, maxSize, null);                                                                 
  +        
  +    }
  +    
  +    public MultipartIterator(HttpServletRequest request,
  +                             int bufferSize,
  +                             long maxSize,
  +                             String tempDir) throws ServletException {
  +                                 
  +        this.request = request;
  +        this.maxSize = maxSize;
  +        if (bufferSize > -1) {
  +            this.bufferSize = bufferSize;
  +        }
  +        if (tempDir != null) {
  +            this.tempDir = tempDir;
  +        }
  +        else {
  +            //default to system-wide tempdir
  +            tempDir = System.getProperty("java.io.tmpdir");
  +        }
  +        parseRequest();
  +    }
  +    
  +    /**
  +     * Retrieves the next element in the iterator if one exists.
  +     *
  +     * @throws a ServletException if the post size exceeds the maximum file size
  +     *         passed in the 3 argument constructor
  +     * @throws an UnsupportedEncodingException if the "ISO-8859-1" encoding isn't found
  +     * @return a {@link org.apache.struts.upload.MultipartElement MultipartElement}
  +     *         representing the next element in the request data
  +     *
  +     */
  +    public MultipartElement getNextElement() throws ServletException, UnsupportedEncodingException {
  +        //retrieve the "Content-Disposition" header
  +        //and parse
  +        String disposition = readLine();
  +        
  +        
  +        if ((disposition != null) && (disposition.startsWith("Content-Disposition"))) {
  +            String name = parseDispositionName(disposition);
  +            String filename = parseDispositionFilename(disposition);
  +                                   
  +            String contentType = null;
  +            boolean isFile = (filename != null);
  +            
  +            if (isFile) {
  +                filename = new File(filename).getName();
  +                
  +                //check for windows filenames,
  +                //from linux jdk's the entire filepath
  +                //isn't parsed correctly from File.getName()
  +                int colonIndex = filename.indexOf(":");
  +                int slashIndex = filename.lastIndexOf("\\");
  +                
  +                if ((colonIndex > -1) && (slashIndex > -1)) {
  +                    //then consider this filename to be a full
  +                    //windows filepath, and parse it accordingly
  +                    //to retrieve just the file name
  +                    filename = filename.substring(slashIndex+1, filename.length());
  +                }
  +            
  +                //get the content type
  +                contentType = readLine();
  +                contentType = parseContentType(contentType);
  +            }
  +            
  +           
  +            
  +            //ignore next line (whitespace) (unless it's a file
  +            //without content-type)
  +	    if (! ((isFile) && contentType == null)) {
  +		readLine();
  +            }
  +            
  +            MultipartElement element = null;
  +            
  +            //process a file element
  +            if (isFile) {
  +                try {
  +                    //create a local file on disk representing the element
  +                    File elementFile = createLocalFile();
  +
  +                    element = new MultipartElement(name, filename, contentType, elementFile);
  +                } catch (IOException ioe) {
  +                    throw new ServletException("IOException while reading file element: ioe.getMessage()", ioe);
  +                }
  +            }
  +            else {
  +                 //read data into String form, then convert to bytes
  +                //for text
  +                StringBuffer textData = new StringBuffer();
  +                String line;
  +                //parse for text data
  +                line = readLine();
  +
  +                while ((line != null) && (!line.startsWith(boundary))) {
  +                    textData.append(line);
  +                    line = readLine();
  +
  +                    if (maxSize > -1) {
  +                        if (totalLength > maxSize) {
  +                            throw new ServletException("Multipart data size exceeds the maximum " +
  +                                "allowed post size");
  +                        }
  +                    }
  +                }
  +
  +                if (textData.length() > 0) {
  +                    //cut off "\r" from the end if necessary
  +                    if (textData.charAt(textData.length()-1) == '\r') {
  +                        textData.setLength(textData.length()-1);
  +                    }
  +                }
  +                
  +                //create the element
  +                element = new MultipartElement(name, textData.toString());
  +            }
  +            return element;
  +        }       
  +        
  +        //reset stream
  +        if (inputStream.markSupported()) {
  +            try {
  +                inputStream.reset();
  +            }
  +            catch (IOException ioe) {
  +                throw new ServletException("IOException while resetting input stream: " +
  +                    ioe.getMessage());
  +            }
  +        }
  +        return null;       
  +    }
  +    
  +    /**
  +     * Set the maximum amount of bytes read from a line at one time
  +     *
  +     * @see javax.servlet.ServletInputStream#readLine(byte[], int, int)
  +     */
  +    public void setBufferSize(int bufferSize) {
  +        this.bufferSize = bufferSize;
  +    }
  +    
  +    /**
  +     * Get the maximum amount of bytes read from a line at one time
  +     *
  +     * @see javax.servlet.ServletInputStream#readLine(byte[], int, int)
  +     */
  +    public int getBufferSize() {
  +        return bufferSize;
  +    }
  +    
  +    /**
  +     * Set the maximum post data size allowed for a multipart request
  +     * @param maxSize The maximum post data size in bytes, set to <code>-1</code>
  +     *                for no limit
  +     */
  +    public void setMaxSize(long maxSize) {
  +        this.maxSize = maxSize;
  +    }
  +    
  +    /** 
  +     * Get the maximum post data size allowed for a multipart request
  +     * @return The maximum post data size in bytes
  +     */
  +    public long getMaxSize() {
  +        return maxSize;
  +    }
  +    
  +    /**
  +     * Handles retrieving the boundary and setting the input stream
  +     */
  +    protected void parseRequest() throws ServletException {
  +             
  +        contentLength = request.getContentLength();
  +        
  +        //set boundary
  +        boundary = parseBoundary(request.getContentType());
  +        boundaryBytes = boundary.getBytes();
  +        
  +        try {
  +            //set the input stream
  +            inputStream = new BufferedMultipartInputStream(request.getInputStream(),
  +                                                           bufferSize,
  +                                                           contentLength,
  +                                                           maxSize);
  +            //mark the input stream to allow multiple reads
  +            if (inputStream.markSupported()) {
  +                inputStream.mark(contentLength+1);
  +            }
  +                
  +        }
  +        catch (IOException ioe) {
  +            throw new ServletException("MultipartIterator.parseRequest(): " +
  +                                       "IOException while trying to obtain " +
  +                                       "ServletInputStream");
  +        }
  +       
  +        if ((boundary == null) || (boundary.length() < 1)) {
  +            //try retrieving the header through more "normal" means
  +            boundary = parseBoundary(request.getHeader("Content-type"));
  +        }
  +        
  +        if ((boundary == null) || (boundary.length() < 1)) {
  +            throw new ServletException("MultipartIterator: cannot retrieve boundary " +
  +                                       "for multipart request");
  +        }
  +        
  +        //read first line
  +        try {
  +	    String firstLine = readLine();
  +
  +	    if (firstLine == null) {
  +		throw new ServletException("MultipartIterator: no multipart request data " +
  +					   "sent");
  +	    }
  +            if (!firstLine.startsWith(boundary)) {
  +                throw new ServletException("MultipartIterator: invalid multipart request " +
  +                                           "data, doesn't start with boundary");
  +            }
  +        }
  +        catch (UnsupportedEncodingException uee) {
  +            throw new ServletException("MultipartIterator: encoding \"ISO-8859-1\" not supported");
  +        }
  +    }
  +      
  +    /**
  +     * Parses a content-type String for the boundary.  Appends a 
  +     * "--" to the beginning of the boundary, because thats the
  +     * real boundary as opposed to the shortened one in the
  +     * content type.
  +     */
  +    public static String parseBoundary(String contentType) {
  +        if (contentType.lastIndexOf("boundary=") != -1) {
  +            String _boundary = "--" + 
  +                               contentType.substring(contentType.lastIndexOf("boundary=")+9);
  +            if (_boundary.endsWith("\n")) {
  +                //strip it off
  +                return _boundary.substring(0, _boundary.length()-1);
  +            }
  +            return _boundary; 
  +        }
  +        return null;      
  +    }
  +    
  +    /**
  +     * Parses the "Content-Type" line of a multipart form for a content type
  +     *
  +     * @param contentTypeString A String reprsenting the Content-Type line, 
  +     *        with a trailing "\n"
  +     * @return The content type specified, or <code>null</code> if one can't be
  +     *         found.
  +     */
  +    public static String parseContentType(String contentTypeString) {
  +        int nameIndex = contentTypeString.indexOf("Content-Type: ");
  +        
  +        if (nameIndex != -1) {
  +            int endLineIndex = contentTypeString.indexOf("\n");
  +            if (endLineIndex == -1) {
  +                endLineIndex = contentTypeString.length()-1;
  +            }
  +            return contentTypeString.substring(nameIndex+14, endLineIndex);
  +        }
  +        return null;
  +    }
  +    
  +    /**
  +     * Retrieves the "name" attribute from a content disposition line
  +     * 
  +     * @param dispositionString The entire "Content-disposition" string
  +     * @return <code>null</code> if no name could be found, otherwise,
  +     *         returns the name
  +     * @see #parseForAttribute(String, String)
  +     */
  +    public static String parseDispositionName(String dispositionString) {
  +        return parseForAttribute("name", dispositionString);
  +    }
  +    
  +    /** 
  +     * Retrieves the "filename" attribute from a content disposition line
  +     *
  +     * @param dispositionString The entire "Content-disposition" string
  +     * @return <code>null</code> if no filename could be found, otherwise,
  +     *         returns the filename
  +     * @see #parseForAttribute(String, String)
  +     */
  +    public static String parseDispositionFilename(String dispositionString) {
  +        return parseForAttribute("filename", dispositionString);
  +    }
  +        
  +    
  +    /**
  +     * Parses a string looking for a attribute-value pair, and returns the value.
  +     * For example:
  +     * <pre>
  +     *      String parseString = "Content-Disposition: filename=\"bob\" name=\"jack\"";
  +     *      MultipartIterator.parseForAttribute(parseString, "name");
  +     * </pre>
  +     * That will return "bob".
  +     * 
  +     * @param attribute The name of the attribute you're trying to get
  +     * @param parseString The string to retrieve the value from
  +     * @return The value of the attribute, or <code>null</code> if none could be found
  +     */
  +    public static String parseForAttribute(String attribute, String parseString) {
  +        int nameIndex = parseString.indexOf(attribute + "=\"");
  +        if (nameIndex != -1) {
  +            
  +            int endQuoteIndex = parseString.indexOf("\"", nameIndex+attribute.length()+3);
  +            
  +            if (endQuoteIndex != -1) {
  +                return parseString.substring(nameIndex+attribute.length()+2, endQuoteIndex);
  +            }
  +            return "";
  +        }        
  +        return null;
  +    }
  +    
  +    /**
  +     * Reads the input stream until it reaches a new line
  +     */
  +    protected String readLine() throws ServletException, UnsupportedEncodingException {
  +       
  +        byte[] bufferByte = new byte[bufferSize];
  +        int bytesRead;
  +        
  +        if (totalLength >= contentLength) {
  +            return null;
  +        }
  +        
  +        try {
  +            bytesRead = inputStream.readLine(bufferByte,
  +                                             0,
  +                                             bufferSize);
  +        }
  +        catch (IOException ioe) {
  +            throw new ServletException("IOException while reading multipart request: " + 
  +				       ioe.getMessage());
  +        }
  +        if (bytesRead == -1) {
  +            return null;
  +        }
  +        
  +        totalLength += bytesRead;
  +        return new String(bufferByte, 0, bytesRead, "ISO-8859-1");
  +    }
  +    
  +    /**
  +     * Creates a file on disk from the current mulitpart element
  +     * @param fileName the name of the multipart file
  +     */
  +    protected File createLocalFile() throws IOException {
  +        
  +        File tempFile = File.createTempFile("strts", null, new File(tempDir));
  +        BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile),
  +                                                            diskBufferSize);
  +        byte[] lineBuffer = new byte[MAX_LINE_SIZE];
  +	int bytesRead = inputStream.readLine(lineBuffer, 0, MAX_LINE_SIZE);
  +        
  +        boolean cutCarriage = false;
  +        boolean cutNewline = false;
  +        
  +        while ((bytesRead != -1) && (!equals(lineBuffer, 0, boundaryBytes.length,
  +                boundaryBytes))) {
  +         
  +                    if (cutCarriage) {
  +                        fos.write('\r');
  +                    }
  +                    if (cutNewline) {
  +                        fos.write('\n');
  +                    }
  +                    if (bytesRead > 0) {
  +                        if (lineBuffer[bytesRead-1] == '\r') {
  +                            bytesRead--;
  +                            cutCarriage = true;
  +                        }
  +                        else {
  +                            cutCarriage = false;
  +                        }
  +                    }
  +                    cutNewline = true;
  +                    fos.write(lineBuffer, 0, bytesRead);
  +                    bytesRead = inputStream.readLine(lineBuffer, 0, MAX_LINE_SIZE);
  +        }
  +        
  +        fos.flush();	
  +        fos.close();
  +        return tempFile;
  +    }
  +    
  +   /**
  +    * Checks bytes for equality.  Two byte arrays are equal if
  +    * each of their elements are the same.  This method checks
  +    * comp[offset] with source[0] to source[length-1] with
  +    * comp[offset + length - 1]
  +    * @param comp The byte to compare to <code>source</code>
  +    * @param offset The offset to start at in <code>comp</code>
  +    * @param length The length of <code>comp</code> to compare to
  +    * @param source The reference byte to test for equality
  +    */
  +   public static boolean equals(byte[] comp, int offset, int length,
  +                                byte[] source) {
  +    
  +       if (length != source.length) {
  +         return false;
  +       }
  +    
  +       for (int i = 0; i < length; i++) {
  +           if (comp[offset+i] != source[i]) {
  +               return false;
  +           }
  +       }
  +       return true;    
  +   }
  +
  +}
  
  
  
  1.1                  jakarta-struts/src/share/org/apache/struts/upload/BufferedMultipartInputStream.java
  
  Index: BufferedMultipartInputStream.java
  ===================================================================
  package org.apache.struts.upload;
  
  import java.io.IOException;
  import java.io.InputStream;
  
  /**
   * This class implements buffering for an InputStream as well as a
   * readLine method.  The purpose of this is to provide a reliable
   * readLine() method.
   */
  public class BufferedMultipartInputStream extends InputStream {
     
      /**
       * The underlying InputStream used by this class
       */
      protected InputStream inputStream;
      
      /**
       * The byte array used to hold buffered data
       */
      protected byte[] buffer;
      
      /**
       * The current offset we're at in the buffer's byte array
       */
      protected int bufferOffset = 0;
      
      /**
       * The size of the byte array buffer
       */
      protected int bufferSize = 8192;
      
      /**
       * The number of bytes read from the underlying InputStream that are
       * in the buffer
       */
      protected int bufferLength = 0;
      
      /**
       * The total number of bytes read so far
       */
      protected int totalLength = 0;
      
      /**
       * The content length of the multipart data
       */
      protected long contentLength;
      
      /**
       * The maximum allowed size for the multipart data, or -1 for an unlimited
       * maximum file length
       */
      protected long maxSize = -1;
      
      
      /**
       * Public constructor for this class, just wraps the InputStream
       * given
       * @param inputStream The underlying stream to read from
       * @param bufferSize The size in bytes of the internal buffer
       * @param contentLength The content length for this request
       * @param maxSize The maximum size in bytes that this multipart
       *                request can be, or -1 for an unlimited length
       */
      public BufferedMultipartInputStream(InputStream inputStream,
                                          int bufferSize,
                                          long contentLength,
                                          long maxSize) throws IOException {
          this.inputStream = inputStream;
          this.bufferSize = bufferSize;
          this.contentLength = contentLength;
          this.maxSize = maxSize;
          buffer = new byte[bufferSize];
          fill();
      }
      
      /**
       * This method returns the number of available bytes left to read
       * in the buffer before it has to be refilled
       */
      public int available() {
          return bufferLength - bufferOffset;
      }
      
      /**
       * This method attempts to close the underlying InputStream
       */
      public void close() throws IOException {
          inputStream.close();
      }
      
      /**
       * This method calls on the mark() method of the underlying InputStream
       */
      public void mark(int position) {
          inputStream.mark(position);  
      } 
      
      /**
       * This method calls on the markSupported() method of the underlying InputStream
       * @return Whether or not the underlying InputStream supports marking
       */
      public boolean markSupported() {
          return inputStream.markSupported();        
      }
      
      /**
       * This method returns the next byte in the buffer, and refills it if necessary.
       * @return The next byte read in the buffer, or -1 if the end of the stream has
       *         been reached
       */
      public int read() throws IOException {        
          if (buffer == null) {
              return -1;
          }
          
          if (bufferOffset < bufferLength) {            
              return (int)(char) buffer[bufferOffset++];            
          }
          fill();
          return read();        
      }
      
      /**
       * This method populates the byte array <code>b</code> with data up to
       * <code>b.length</code> bytes
       */
      public int read(byte[] b) throws IOException {
          return read(b, 0, b.length);        
      }
      
      /**
       * This method populates the byte array <code>b</code> with data up to 
       * <code>length</code> starting at b[offset]
       */
      public int read(byte[] b, int offset, int length) throws IOException {
          
          int count = 0;
          int read = read();
          if (read == -1) {
              return -1;
          }
          
          while ((read != -1) && (count < length)) {
              b[offset] = (byte) read;
              read = read();
              count++;
              offset++;
          }
          return count;
      }
      
      /**
       * This method reads into the byte array <code>b</code> until
       * a newline ('\n') character is encountered or the number of bytes
       * specified by <code>length</code> have been read
       */
      public int readLine(byte[] b, int offset, int length) throws IOException {
          
          int count = 0;
          int read = read();
          if (read == -1) {
              return -1;
          }
          
          while ((read != -1) && (count < length)) {
              if (read == '\n')
                  break;
              b[offset] = (byte) read;
              count++;
              offset++;
              read = read();
          }
          return count;
      }
      
      /**
       * This method makes a call to the reset() method of the underlying
       * InputStream
       */
      public void reset() throws IOException {
          inputStream.reset();        
      }
  
      /**
       * Fills the buffer with data from the underlying inputStream.  If it can't
       * fill the entire buffer in one read, it will read as many times as necessary
       * to fill the buffer
       */
      protected void fill() throws IOException {
  
          if ((bufferOffset > -1) && (bufferLength > -1)) {
              int length = Math.min(bufferSize, (int) contentLength);
              if (maxSize > -1) {
                  length = Math.min(length, (int) maxSize);
              }
  
              int bytesRead = inputStream.read(buffer, 0, length);
              if (bytesRead == -1) {
                  buffer = null;
                  bufferOffset = -1;
                  bufferLength = -1;
              }
              else {
                  bufferLength = bytesRead;
                  totalLength += bytesRead;
                  bufferOffset = 0;
              }
          }
      }
  }
  
  
  1.2       +37 -8     jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadAction.java
  
  Index: UploadAction.java
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadAction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- UploadAction.java	2001/03/22 13:17:10	1.1
  +++ UploadAction.java	2001/04/11 22:56:23	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadAction.java,v 1.1 2001/03/22 13:17:10 rleland Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/03/22 13:17:10 $
  + * $Header: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadAction.java,v 1.2 2001/04/11 22:56:23 mschachter Exp $
  + * $Revision: 1.2 $
  + * $Date: 2001/04/11 22:56:23 $
    *
    * ====================================================================
    *
  @@ -64,6 +64,8 @@
   
   import java.io.InputStream;
   import java.io.IOException;
  +import java.io.OutputStream;
  +import java.io.FileOutputStream;
   import java.io.ByteArrayOutputStream;
   import java.io.FileNotFoundException;
   
  @@ -85,7 +87,7 @@
    * page to display them
    *
    * @author Mike Schachter
  - * @version $Revision: 1.1 $ $Date: 2001/03/22 13:17:10 $
  + * @version $Revision: 1.2 $ $Date: 2001/04/11 22:56:23 $
    */
   
   
  @@ -111,6 +113,8 @@
   
                           //retrieve the content type
                           String contentType = file.getContentType();
  +                        
  +                        boolean writeFile = theForm.getWriteFile();
   
                           //retrieve the file size
                           String size = (file.getFileSize() + " bytes");
  @@ -120,12 +124,37 @@
                           try {
                                   //retrieve the file data
                                   ByteArrayOutputStream baos = new ByteArrayOutputStream();
  -
                                   InputStream stream = file.getInputStream();
  -                                byte[] buffer = new byte[file.getFileSize()];
  -                                stream.read(buffer);
  -                                baos.write(buffer);
  -                                data = new String(baos.toByteArray());
  +                                if (!writeFile) {
  +                                    //only write files out that are less than 1MB
  +                                    if (file.getFileSize() < (4*1024000)) {
  +
  +                                        byte[] buffer = new byte[8192];
  +                                        int bytesRead = 0;
  +                                        while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) {
  +                                            baos.write(buffer, 0, bytesRead);
  +                                        }
  +                                        data = new String(baos.toByteArray());
  +                                    }
  +                                    else {
  +                                        data = new String("The file is greater than 4MB, " +
  +                                            " and has not been written to stream." +
  +                                            " File Size: " + file.getFileSize() + " bytes. This is a" +
  +                                            " limitation of this particular web application, hard-coded" +
  +                                            " in org.apache.struts.upload.UploadAction");
  +                                    }
  +                                }
  +                                else {
  +                                    //write the file to the file specified
  +                                    OutputStream bos = new FileOutputStream(theForm.getFilePath());
  +                                    int bytesRead = 0;
  +                                    byte[] buffer = new byte[8192];
  +                                    while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) {
  +                                        bos.write(buffer, 0, bytesRead);
  +                                    }
  +                                    bos.close();
  +                                    data = "The file has been written to \"" + theForm.getFilePath() + "\"";
  +                                }
                           }
                           catch (FileNotFoundException fnfe) {
                                   return null;
  
  
  
  1.2       +45 -3     jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadForm.java
  
  Index: UploadForm.java
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadForm.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- UploadForm.java	2001/03/22 13:17:10	1.1
  +++ UploadForm.java	2001/04/11 22:56:24	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadForm.java,v 1.1 2001/03/22 13:17:10 rleland Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/03/22 13:17:10 $
  + * $Header: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadForm.java,v 1.2 2001/04/11 22:56:24 mschachter Exp $
  + * $Revision: 1.2 $
  + * $Date: 2001/04/11 22:56:24 $
    *
    * ====================================================================
    *
  @@ -74,7 +74,7 @@
    * that struts uses is org.apache.struts.upload.DiskMultipartRequestHandler.
    *
    * @author Mike Schachter
  - * @version $Revision: 1.1 $ $Date: 2001/03/22 13:17:10 $
  + * @version $Revision: 1.2 $ $Date: 2001/04/11 22:56:24 $
    */
   
   public class UploadForm extends ActionForm {
  @@ -83,11 +83,21 @@
            * The value of the text the user has sent as form data
            */
           protected String theText;
  +        
  +        /**
  +         * Whether or not to write to a file
  +         */
  +        protected boolean writeFile;
   
           /**
            * The file that the user has uploaded
            */
           protected FormFile theFile;
  +        
  +        /**
  +         * The file path to write to
  +         */
  +        protected String filePath;
   
   
   
  @@ -117,5 +127,37 @@
            */
           public void setTheFile(FormFile theFile) {
                   this.theFile = theFile;
  +        }
  +        
  +        /**
  +         * Set whether or not to write to a file
  +         */
  +        public void setWriteFile(boolean writeFile) {
  +            this.writeFile = writeFile;
  +        } 
  +        
  +        /**
  +         * Get whether or not to write to a file
  +         */
  +        public boolean getWriteFile() {
  +            return writeFile;
  +        }
  +        
  +        /**
  +         * Set the path to write a file to
  +         */
  +        public void setFilePath(String filePath) {
  +            this.filePath = filePath;
  +        }
  +        
  +        /**
  +         * Get the path to write a file to
  +         */
  +        public String getFilePath() {
  +            return filePath;
  +        }
  +        
  +        public void reset() {
  +            writeFile = false;
           }
   }
  
  
  
  1.2       +14 -14    jakarta-struts/web/upload/display.jsp
  
  Index: display.jsp
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/web/upload/display.jsp,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- display.jsp	2000/12/19 19:23:05	1.1
  +++ display.jsp	2001/04/11 22:56:27	1.2
  @@ -1,15 +1,15 @@
  -<%@ page language="java" %>
  -
  -<b>The Text</b>: <%= request.getAttribute("text") %> <br />
  -
  -<b>The File name</b>: <%= request.getAttribute("fileName") %> <br />
  -
  -<b>The File content type</b>: <%= request.getAttribute("contentType") %> <br />
  -
  -<b>The File size</b>: <%= request.getAttribute("size") %> <br />
  -
  -<b>The File data</b>: <br />
  -
  -<hr />
  -<pre><%= request.getAttribute("data") %></pre>
  +<%@ page language="java" %>
  +
  +<b>The Text</b>: <%= request.getAttribute("text") %> <br />
  +
  +<b>The File name</b>: <%= request.getAttribute("fileName") %> <br />
  +
  +<b>The File content type</b>: <%= request.getAttribute("contentType") %> <br />
  +
  +<b>The File size</b>: <%= request.getAttribute("size") %> <br />
  +
  +<b>The File data</b>: <br />
  +
  +<hr />
  +<pre><%= request.getAttribute("data") %></pre>
   <hr />
  
  
  
  1.3       +6 -0      jakarta-struts/web/upload/upload.jsp
  
  Index: upload.jsp
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/web/upload/upload.jsp,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- upload.jsp	2001/01/07 00:16:29	1.2
  +++ upload.jsp	2001/04/11 22:56:28	1.3
  @@ -15,6 +15,12 @@
   	
   	Please select the file that you would like to upload:<br />
   	<html:file property="theFile" /><br /><br />
  +
  +        If you would rather write this file to another file, please check here:
  +        <html:checkbox property="writeFile" /><br /><br />
  +
  +        If you checked the box to write to a file, please specify the file path here:<br />
  +        <html:text property="filePath" /><br /><br />
   	
   	<html:submit />