You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by "Schachter, Michael" <MS...@Bluestone.com> on 2001/02/15 00:27:13 UTC

RE: Breaking Interface (was:Re: cvs commit: jakarta-struts/web/u pload/WEB-INF/classes/org/apache/struts/example/upload UploadAction.java )

 I'd rather not "not break it", as it's an important change.  On average,
I'm usually pretty concerned about not breaking backwards compatibility. 

 But this is only for people who have their own implementation of
org.apache.struts.upload.FormFile, which I would assume are few and far
between.   It's also a very small change to someone's implementation to
support this. 

 I'll change it (probably by putting the method just in the implementation
and making people cast their FormFile instances into DiskFile's to use it)
if there is anybody out there that requests it.

-----Original Message-----
From: Rob Leland
To: struts-dev@jakarta.apache.org
Sent: 2/14/01 5:00 PM
Subject: Breaking Interface (was:Re: cvs commit:
jakarta-struts/web/upload/WEB-INF/classes/org/apache/struts/example/upload
UploadAction.java)

> 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 ?

mschachter@apache.org 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.jav
a,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/DiskMultipartRequestHa
ndler.java
> 
>   Index: DiskMultipartRequestHandler.java
>   ===================================================================
>   RCS file:
/home/cvs/jakarta-struts/src/share/org/apache/struts/upload/DiskMultipar
tRequestHandler.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.jav
a,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/MultipartEle
ment.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/MultipartIte
rator.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.j
ava
> 
>   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/uplo
ad/UploadAction.java
> 
>   Index: UploadAction.java
>   ===================================================================
>   RCS file:
/home/cvs/jakarta-struts/web/upload/WEB-INF/classes/org/apache/struts/ex
ample/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;
> 
> 
>