> This commit will break any code that implements the
>  org.apache.struts.upload.FormFile interface, because a new method was
>  added.

Is there a way to not break this for people ?
One of the things Struts/Craig has always stressed
is good backward compatability ?

[EMAIL PROTECTED] wrote:
> 
> mschachter    01/02/14 13:43:10
> 
>   Modified:    src/share/org/apache/struts/upload DiskFile.java
>                         DiskMultipartRequestHandler.java FormFile.java
>                         MultipartElement.java MultipartIterator.java
>                web/upload/WEB-INF/classes/org/apache/struts/example/upload
>                         UploadAction.java
>   Added:       src/share/org/apache/struts/upload MultipartValueStream.java
>   Log:
>   Made changes to classes in upload package to address problems with storing
>   files in memory.  This commit will break any code that implements the
>   org.apache.struts.upload.FormFile interface, because a new method was
>   added. Changed MultipartIterator to use the new MultipartValueStream class
>   from Jimmy Larsson  for
>   going through multipart elements.  This will fix a bug with null content types
>   for files. Also updated the upload example to use the new recommended method
>   for retrieving file content.
> 
>   Submitted By: Jimmy Larsson
> 
>   Revision  Changes    Path
>   1.2       +14 -5     
>jakarta-struts/src/share/org/apache/struts/upload/DiskFile.java
> 
>   Index: DiskFile.java
>   ===================================================================
>   RCS file: 
>/home/cvs/jakarta-struts/src/share/org/apache/struts/upload/DiskFile.java,v
>   retrieving revision 1.1
>   retrieving revision 1.2
>   diff -u -r1.1 -r1.2
>   --- DiskFile.java     2000/11/09 20:08:56     1.1
>   +++ DiskFile.java     2001/02/14 21:43:05     1.2
>   @@ -1,6 +1,7 @@
>    package org.apache.struts.upload;
> 
>    import java.io.File;
>   +import java.io.InputStream;
>    import java.io.IOException;
>    import java.io.FileInputStream;
>    import java.io.ByteArrayOutputStream;
>   @@ -26,10 +27,8 @@
>        /**
>         * The name of the file
>         */
>   -    protected String fileName;
>   +    protected String fileName;
> 
>   -
>   -
>        public DiskFile(String filePath) {
>            this.filePath = filePath;
>        }
>   @@ -39,6 +38,9 @@
>         * array form.  Tries to read the entire file (using a byte array
>         * the size of getFileSize()) at once, in one call to 
>FileInputStream.read(byte[]).
>         * For buffered reading, see {@link #getFileData(int) getFileData(int)}.
>   +     * Note that this method can be dangerous, and that the size of a file
>   +     * can cause an OutOfMemoryError quite easily.  You should use
>   +     * {@link #getInputStream() getInputStream} and do your own thing.
>         *
>         * @exception ServletException If the temp file no longer exists, or if there 
>is
>         *                    some sort of IOException
>   @@ -56,6 +58,9 @@
> 
>        /**
>         * Attempts to read a file n bytes at a time, n being equal to "bufferSize".
>   +     * Note that this method can be dangerous, and that the size of a file
>   +     * can cause an OutOfMemoryError quite easily.  You should use
>   +     * {@link #getInputStream() getInputStream} and do your own thing.
>         *
>         * @param bufferSize The size in bytes that are read from the file at a time
>         * @exception FileNotFoundException If the temp file no longer exists
>   @@ -150,7 +155,11 @@
>        public int getFileSize() {
>            return fileSize;
>        }
>   -
>   -
> 
>   +    /**
>   +     * Returns a FileInputStream to the file
>   +     */
>   +    public InputStream getInputStream() throws FileNotFoundException, IOException 
>{
>   +        return new FileInputStream(filePath);
>   +    }
>    }
> 
> 
> 
>   1.7       +55 -97    
>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.6
>   retrieving revision 1.7
>   diff -u -r1.6 -r1.7
>   --- DiskMultipartRequestHandler.java  2000/11/28 07:17:17     1.6
>   +++ DiskMultipartRequestHandler.java  2001/02/14 21:43:05     1.7
>   @@ -47,11 +47,19 @@
>         */
>        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());
>   +                                                            
>getMaxSizeFromServlet(),
>   +                                                            tempDir);
>            MultipartElement element;
> 
>            textElements = new Hashtable();
>   @@ -60,29 +68,21 @@
> 
>            try {
>                while ((element = iterator.getNextElement()) != null) {
>   -                if ((element.getFileName() == null) && (element.getContentType() 
>== null)) {
>   -                    String textData;
>   -                    try {
>   -                        textData = new String(element.getData(), "ISO-8859-1");
>   -                    }
>   -                    catch (UnsupportedEncodingException uee) {
>   -                        textData = new String(element.getData());
>   -                    }
>   -
>   -                    textElements.put(element.getName(), textData);
>   -                    allElements.put(element.getName(), textData);
>   +                if (!element.isFile()) {
>   +                    textElements.put(element.getName(), element.getValue());
>   +                    allElements.put(element.getName(), element.getValue());
>                    }
>                    else {
>   -                    try {
>   -                        DiskFile theFile = writeFile(element);
>   -                        fileElements.put(element.getName(), theFile);
>   -                        allElements.put(element.getName(), theFile);
>   -                    }
>   -                    catch (IOException ioe) {
>   -                        throw new ServletException("DiskMultipartRequestHandler." 
>+
>   -                        "handleRequest(), IOException: " +
>   -                        ioe.getMessage());
>   -                    }
>   +
>   +                     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);
>   +                     }
>                    }
>                }
>            }
>   @@ -92,81 +92,6 @@
> 
>        }
> 
>   -
>   -    protected DiskFile writeFile(MultipartElement element) throws IOException,
>   -    ServletException {
>   -        DiskFile theFile = null;
>   -
>   -        //get a handle to some temporary file and open
>   -        //a stream to it
>   -        String 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);
>   -                }
>   -            }
>   -        }
>   -
>   -        File tempDirectory = new File(tempDir);
>   -
>   -        if ((!tempDirectory.exists()) || (!tempDirectory.isDirectory())) {
>   -            throw new ServletException("DiskMultipartRequestHandler: no " +
>   -            "temporary directory specified for disk write");
>   -        }
>   -
>   -        File tempFile = File.createTempFile("strts", null, tempDirectory);
>   -        FileOutputStream fos = new FileOutputStream(tempFile);
>   -
>   -        //int bufferSize = servlet.getBufferSize();
>   -        int bufferSize = -1;
>   -        if (bufferSize > 0) {
>   -            //buffer the write if servlet.getBufferSize() > 0
>   -            ByteArrayInputStream byteArray = new 
>ByteArrayInputStream(element.getData());
>   -            byte[] bufferBytes = new byte[bufferSize];
>   -            int bytesWritten = 0;
>   -            int bytesRead = 0;
>   -            int offset = 0;
>   -
>   -            while ((bytesRead = byteArray.read(bufferBytes,
>   -            offset, bufferSize)) != -1) {
>   -                fos.write(bufferBytes, offset, bytesRead);
>   -                bytesWritten += bytesRead;
>   -                offset += bytesRead;
>   -            }
>   -            byteArray.close();
>   -        }
>   -        else {
>   -            //write in one big chunk
>   -            fos.write(element.getData());
>   -        }
>   -
>   -        theFile = new DiskFile(tempFile.getAbsolutePath());
>   -        theFile.setContentType(element.getContentType());
>   -        theFile.setFileName(element.getFileName());
>   -        theFile.setFileSize(element.getData().length);
>   -
>   -        fos.close();
>   -
>   -        return theFile;
>   -    }
>   -
>        public Hashtable getAllElements() {
>            return allElements;
>        }
>   @@ -248,4 +173,37 @@
> 
>            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.2       +15 -4     
>jakarta-struts/src/share/org/apache/struts/upload/FormFile.java
> 
>   Index: FormFile.java
>   ===================================================================
>   RCS file: 
>/home/cvs/jakarta-struts/src/share/org/apache/struts/upload/FormFile.java,v
>   retrieving revision 1.1
>   retrieving revision 1.2
>   diff -u -r1.1 -r1.2
>   --- FormFile.java     2000/11/09 20:08:56     1.1
>   +++ FormFile.java     2001/02/14 21:43:05     1.2
>   @@ -1,5 +1,6 @@
>    package org.apache.struts.upload;
> 
>   +import java.io.InputStream;
>    import java.io.IOException;
>    import java.io.FileNotFoundException;
> 
>   @@ -45,18 +46,28 @@
>        public void setFileName(String fileName);
> 
>        /**
>   -     * Get the data in byte array for
>   -     * for this file
>   +     * Get the data in byte array for for this file.  Note that this can be
>   +     * a very hazardous method, files can be large enough to cause
>   +     * OutOfMemoryErrors.  Short of being deprecated, it's strongly recommended
>   +     * that you use {@link #getInputStream() getInputStream} to get the file
>   +     * data.
>         *
>         * @exception FileNotFoundException If some sort of file representation
>         *                                  cannot be found for the FormFile
>   -     *
>         * @exception IOException If there is some sort of IOException
>   -     *
>         * @return An array of bytes representing the data contained
>         *         in the form file
>         */
>        public byte[] getFileData() throws FileNotFoundException, IOException;
>   +
>   +    /**
>   +     * Get an InputStream that represents this file.  This is the preferred
>   +     * method of getting file data.
>   +     * @exception FileNotFoundException If some sort of file representation
>   +     *                                  cannot be found for the FormFile
>   +     * @exception IOException If there is some sort of IOException
>   +     */
>   +    public InputStream getInputStream() throws FileNotFoundException, IOException;
> 
>        /**
>         * Destroy all content for this form file.
> 
> 
> 
>   1.2       +98 -0     
>jakarta-struts/src/share/org/apache/struts/upload/MultipartElement.java
> 
>   Index: MultipartElement.java
>   ===================================================================
>   RCS file: 
>/home/cvs/jakarta-struts/src/share/org/apache/struts/upload/MultipartElement.java,v
>   retrieving revision 1.1
>   retrieving revision 1.2
>   diff -u -r1.1 -r1.2
>   --- MultipartElement.java     2000/11/09 20:08:56     1.1
>   +++ MultipartElement.java     2001/02/14 21:43:06     1.2
>   @@ -1,5 +1,7 @@
>    package org.apache.struts.upload;
> 
>   +import java.io.File;
>   +
>    /**
>     * This class represents an element in a multipart request.
>     * It has a few methods for determining
>   @@ -20,10 +22,16 @@
> 
>        /**
>         * The element data
>   +     * @deprecated This should never be used.
>         */
>        protected byte[] data;
> 
>        /**
>   +     * The element's data represented in a (possibly temporary) file
>   +     */
>   +    protected File file;
>   +
>   +    /**
>         * The element name
>         */
>        protected String name;
>   @@ -33,15 +41,65 @@
>         */
>        protected String fileName;
> 
>   +    /**
>   +     * The element's text value, null for file elements
>   +     */
>   +    protected String value;
>   +
>   +    /**
>   +     * Whether or not this element is a file
>   +     */
>   +    protected boolean isFile = false;
> 
>   +    /**
>   +     * @deprecated Use the constructor that takes an File as an argument
>   +     *             as opposed to a byte array argument, which can cause
>   +     *             memory problems
>   +     */
>        public MultipartElement(String name, String fileName, String contentType, 
>byte[] data) {
>            this.name = name;
>            this.fileName = fileName;
>            this.contentType = contentType;
>            this.data = data;
>   +
>   +        if (fileName != null) {
>   +            isFile = true;
>   +        }
>        }
> 
>        /**
>   +     * Constructor for a file element
>   +     * @param name The form name of the element
>   +     * @param fileName The file name of the element if this element is a file
>   +     * @param contentType The content type of the element if a file
>   +     * @param file The (possibly temporary) file representing this element if
>   +     *             it's a file
>   +     */
>   +    public MultipartElement(String name,
>   +                            String fileName,
>   +                            String contentType,
>   +                            File file) {
>   +
>   +        this.name = name;
>   +        this.fileName = fileName;
>   +        this.contentType = contentType;
>   +        this.file = file;
>   +        this.isFile = true;
>   +    }
>   +
>   +    /**
>   +     * Constructor for a text element
>   +     * @param name The name of the element
>   +     * @param value The value of the element
>   +     */
>   +    public MultipartElement(String name, String value) {
>   +        this.name = name;
>   +        this.value = value;
>   +        this.isFile = false;
>   +    }
>   +
>   +
>   +    /**
>         * Retrieve the content type
>         */
>        public String getContentType() {
>   @@ -51,12 +109,21 @@
> 
>        /**
>         * Retrieve the data
>   +     * @deprecated Use the getFile method to get a File representing the
>   +     *             data for this element
>         */
>        public byte[] getData() {
>            return data;
>        }
> 
>        /**
>   +     * Get the File that holds the data for this element.
>   +     */
>   +    public File getFile() {
>   +        return file;
>   +    }
>   +
>   +    /**
>         * Retrieve the name
>         */
>        public String getName() {
>   @@ -71,6 +138,22 @@
>            return fileName;
>        }
> 
>   +    /**
>   +     * Returns the value of this multipart element
>   +     * @return A String if the element is a text element, <code>null</code>
>   +     *         otherwise
>   +     */
>   +    public String getValue() {
>   +        return value;
>   +    }
>   +
>   +    /**
>   +     * Set the file that represents this element
>   +     */
>   +    public void setFile(File file) {
>   +        this.file = file;
>   +    }
>   +
>        /**
>         * Set the file name for this element
>         */
>   @@ -92,9 +175,24 @@
>            this.contentType = contentType;
>        }
> 
>   +    /**
>   +     * Is this element a file?
>   +     */
>   +    public boolean isFile() {
>   +        if (file == null) {
>   +            return false;
>   +        }
>   +        return true;
>   +    }
>   +
>   +    public void setValue(String value) {
>   +        this.value = value;
>   +    }
> 
>        /**
>         * Set the data
>   +     * @deprecated Use the setFile method to set the file
>   +     *             that represents the data of this element
>         */
>        public void setData(byte[] data) {
>            this.data = data;
> 
> 
> 
>   1.9       +111 -32   
>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.8
>   retrieving revision 1.9
>   diff -u -r1.8 -r1.9
>   --- MultipartIterator.java    2001/01/05 21:51:35     1.8
>   +++ MultipartIterator.java    2001/02/14 21:43:06     1.9
>   @@ -1,7 +1,10 @@
>    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;
>   @@ -72,6 +75,11 @@
>         * 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
>   @@ -106,11 +114,28 @@
>         */
>        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();
>        }
> 
>   @@ -134,8 +159,9 @@
>                String filename = parseDispositionFilename(disposition);
> 
>                String contentType = null;
>   -
>   -            if (filename != null) {
>   +            boolean isFile = (filename != null);
>   +
>   +            if (isFile) {
>                    filename = new File(filename).getName();
> 
>                    //check for windows filenames,
>   @@ -158,38 +184,55 @@
>                    contentType = parseContentType(contentType);
>                }
> 
>   -            //read data into String form, then convert to bytes
>   -            //for both normal text and file
>   -            StringBuffer textData = new StringBuffer();
>   -            String line;
>   +
> 
>   -            //ignore next line (whitespace)
>   -            readLine();
>   +            //ignore next line (whitespace) (unless it's a file
>   +            //without content-type)
>   +         if (! ((isFile) && contentType == null)) {
>   +             readLine();
>   +            }
>   +
>   +            MultipartElement element = null;
> 
>   -            //parse for text data
>   -            line = readLine();
>   -
>   -            while ((line != null) && (!line.startsWith(boundary))) {
>   -                textData.append(line);
>   +            //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();
>   -
>   -                if (maxSize > -1) {
>   -                    if (totalLength > maxSize) {
>   -                        throw new ServletException("Multipart data size exceeds 
>the maximum " +
>   -                            "allowed post size");
>   +
>   +                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());
>                }
>   -
>   -            if (textData.length() > 1) {
>   -                //cut off "\r\n" from the end
>   -                textData.setLength(textData.length()-2);
>   -            }
>   -
>   -            MultipartElement element = new MultipartElement(name,
>   -                                                            filename,
>   -                                                            contentType,
>   -                                                            
>textData.toString().getBytes("ISO-8859-1"));
>                return element;
>            }
>            //reset stream
>   @@ -331,10 +374,6 @@
>            }
>            return null;
>        }
>   -
>   -
>   -
>   -
> 
>        /**
>         * Retrieves the "name" attribute from a content disposition line
>   @@ -416,4 +455,44 @@
>            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;
>   +    }
>   +
>    }
> 
> 
> 
>   1.1                  
>jakarta-struts/src/share/org/apache/struts/upload/MultipartValueStream.java
> 
>   Index: MultipartValueStream.java
>   ===================================================================
>   package org.apache.struts.upload;
> 
>   import java.io.*;
>   import java.util.*;
> 
>   import javax.servlet.ServletException;
> 
>   /**
>    * This class implements an inputStream that reads another stream until
>    * a multipart boundary is found. The class reports eof when boundary found.
>    * The undelying stream is not closed.
>    *
>    * <p>
>    * See RFC 1867 (http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1867.txt)
>    * for details about the protocol.
>    * <p>
>    *
>    * @author Jimmy Larsson
>    */
> 
>   class MultipartValueStream extends InputStream {
> 
>       public static final String HEADER_ENCODING = "iso-8859-1";
> 
>       /** the underlying stream */
>       private InputStream in;
> 
>       /** byte buffer with the boundary */
>       private byte boundaryBytes[];
> 
>       /** how many curretly matched boundary bytes? */
>       private int matchedBoundaryBytes;
> 
>       /** the read ahead buffer (cyclic) */
>       private byte readAheadBytes[];
> 
>       /** The start index for the read ahead cyclic buffer (points to the first 
>byte) */
>       private int readAheadBufferStartI;
> 
>       /** The end index for the read ahead cyclic buffer (points to the last byte) */
>       private int readAheadBufferEndI;
> 
>       /** have we reached the boundary? */
>       private boolean boundaryReached = false;
> 
>       /** is the boundary found a final boundary? */
>       private boolean finalBoundaryReached = false;
> 
> 
>       /**
>        * Create a stream that stops reading at the boundary
>        *
>        * NOTE: the boundary parameter is without the trailing dashes "--".
>        */
>       public MultipartValueStream(InputStream in, String boundary)
>         throws IOException
>       {
>         this.in = in;
>         this.boundaryBytes = ("\r\n" + boundary).getBytes(HEADER_ENCODING);
>         this.matchedBoundaryBytes = 0;
>         this.readAheadBytes = new byte[this.boundaryBytes.length];
> 
>         /* Fill read ahead buffer */
>         if (in.read(readAheadBytes, 0, readAheadBytes.length) != 
>readAheadBytes.length) {
>             throw new IOException("end of stream before boundary found!");
>         }
> 
>         /* Count the number of matched chars */
>         for (int i = 0; i < readAheadBytes.length; i++) {
>             if (readAheadBytes[i] == boundaryBytes[matchedBoundaryBytes]) {
>                 matchedBoundaryBytes++;
>             } else {
>                 matchedBoundaryBytes = 0;
>                 if (readAheadBytes[i] == boundaryBytes[0]) {
>                     matchedBoundaryBytes = 1;
>                 }
>             }
>         }
> 
>         readAheadBufferStartI = 0;
>         readAheadBufferEndI = readAheadBytes.length - 1;
>       }
> 
> 
>       /**
>        * Read the next byte
>        *
>        * @return -1 on boundary reached
>        * @exception IOException if the ending boundary is never found
>        *
>        */
> 
>       public int read() throws IOException {
>         if (boundaryReached) {
>             return -1;
>         }
>         if (matchedBoundaryBytes == boundaryBytes.length) {
> 
>             boundaryReached = true;
> 
>             /*
>              * Boundary found...
>              *
>              * Read two more bytes:
>              * 1. the bytes are "--":          this is the last parameter value 
>(then read "\r\n" too)
>              * 2. the bytes are "\r\n":        this is not the last value
>              * 3. the bytes are somthing else: Exception
>              */
> 
>             byte buf[] = new byte[2];
>             if (in.read(buf) != 2) {
>                 throw new IOException("end of stream before boundary found!");
>             }
> 
>             String readStr = new String(buf, HEADER_ENCODING);
>             if (readStr.equals("--")) {
> 
>                 if (in.read(buf) != 2) {
>                     throw new IOException("invalid end of final boundary found!");
>                 }
>                 readStr = new String(buf, HEADER_ENCODING);
>                 if (!readStr.equals("\r\n")) {
>                     throw new IOException("invalid end of final boundary found!");
>                 }
>                 finalBoundaryReached = true;
> 
>             } else if (readStr.equals("\r\n")) {
>                 finalBoundaryReached = false;
>             } else {
>                 throw new IOException("invalid end of boundary found!");
>             }
> 
>             return -1;
>         }
> 
>         /*
>          * Might seem odd, but we are supposed to return
>          * a byte as an int in range 0 - 255, and the byte type
>          * is signed (-128 to 127)
>          *
>          */
>         int returnByte = (int)(char) readAheadBytes[readAheadBufferStartI];
> 
>         /* Move cyclic-buffers start pointer */
>         readAheadBufferStartI++;
>         if (readAheadBufferStartI == readAheadBytes.length) {
>             readAheadBufferStartI = 0;
>         }
> 
>         /* read from the underlying stream */
>         int underlyingRead = in.read();
>         if (underlyingRead == -1) {
>             throw new IOException("end of stream before boundary found!");
>         }
> 
>         /* Move cyclic-buffers end pointer */
>         readAheadBufferEndI++;
>         if (readAheadBufferEndI == readAheadBytes.length) {
>             readAheadBufferEndI = 0;
>         }
>         readAheadBytes[readAheadBufferEndI] = (byte) underlyingRead;
> 
>         if (readAheadBytes[readAheadBufferEndI] == 
>boundaryBytes[matchedBoundaryBytes]) {
>             matchedBoundaryBytes++;
>         } else {
>             matchedBoundaryBytes = 0;
>             if (readAheadBytes[readAheadBufferEndI] == boundaryBytes[0]) {
>                 matchedBoundaryBytes = 1;
>             }
>         }
>         return returnByte;
>       }
> 
>       /**
>        * @return true if we are the last stream, ie. we encountered a final boundary
>        * @return false otherwise
>        *
>        * @exception ParameterException if the boundary has not yet been reached
>        */
> 
>       public boolean encounteredFinalBoundary()
>         throws ServletException
>       {
>         if (!boundaryReached) {
>             throw new ServletException("have not reached boundary yet!");
>         }
>         return finalBoundaryReached;
>       }
>   }
> 
> 
> 
>   1.2       +12 -4     
>jakarta-struts/web/upload/WEB-INF/classes/org/apache/struts/example/upload/UploadAction.java
> 
>   Index: UploadAction.java
>   ===================================================================
>   RCS file: 
>/home/cvs/jakarta-struts/web/upload/WEB-INF/classes/org/apache/struts/example/upload/UploadAction.java,v
>   retrieving revision 1.1
>   retrieving revision 1.2
>   diff -u -r1.1 -r1.2
>   --- UploadAction.java 2000/12/19 19:23:07     1.1
>   +++ UploadAction.java 2001/02/14 21:43:09     1.2
>   @@ -1,6 +1,8 @@
>    package org.apache.struts.example.upload;
> 
>   +import java.io.InputStream;
>    import java.io.IOException;
>   +import java.io.ByteArrayOutputStream;
>    import java.io.FileNotFoundException;
> 
>    import javax.servlet.http.HttpServletRequest;
>   @@ -27,9 +29,9 @@
>    public class UploadAction extends Action {
> 
>         public ActionForward perform(ActionMapping mapping,
>   -                                                                               
>ActionForm    form,
>   -                                                                               
>HttpServletRequest request,
>   -                                                                               
>HttpServletResponse response) {
>   +                                  ActionForm    form,
>   +                                  HttpServletRequest request,
>   +                                  HttpServletResponse response) {
> 
>                 if (form instanceof UploadForm) {
> 
>   @@ -54,7 +56,13 @@
> 
>                         try {
>                                 //retrieve the file data
>   -                             data = new String(file.getFileData());
>   +                             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());
>                         }
>                         catch (FileNotFoundException fnfe) {
>                                 return null;
> 
> 
>

Reply via email to