Added: 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java?rev=412780&view=auto
==============================================================================
--- 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
 (added)
+++ 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
 Thu Jun  8 08:35:56 2006
@@ -0,0 +1,891 @@
+/*
+ * Copyright 2001-2006 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tomcat.util.http.fileupload;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+
+/**
+ * <p> Low level API for processing file uploads.
+ *
+ * <p> This class can be used to process data streams conforming to MIME
+ * 'multipart' format as defined in
+ * <a href="http://www.ietf.org/rfc/rfc1867.txt";>RFC 1867</a>. Arbitrarily
+ * large amounts of data in the stream can be processed under constant
+ * memory usage.
+ *
+ * <p> The format of the stream is defined in the following way:<br>
+ *
+ * <code>
+ *   multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
+ *   encapsulation := delimiter body CRLF<br>
+ *   delimiter := "--" boundary CRLF<br>
+ *   close-delimiter := "--" boudary "--"<br>
+ *   preamble := &lt;ignore&gt;<br>
+ *   epilogue := &lt;ignore&gt;<br>
+ *   body := header-part CRLF body-part<br>
+ *   header-part := 1*header CRLF<br>
+ *   header := header-name ":" header-value<br>
+ *   header-name := &lt;printable ascii characters except ":"&gt;<br>
+ *   header-value := &lt;any ascii characters except CR & LF&gt;<br>
+ *   body-data := &lt;arbitrary data&gt;<br>
+ * </code>
+ *
+ * <p>Note that body-data can contain another mulipart entity.  There
+ * is limited support for single pass processing of such nested
+ * streams.  The nested stream is <strong>required</strong> to have a
+ * boundary token of the same length as the parent stream (see [EMAIL 
PROTECTED]
+ * #setBoundary(byte[])}).
+ *
+ * <p>Here is an exaple of usage of this class.<br>
+ *
+ * <pre>
+ *    try {
+ *        MultipartStream multipartStream = new MultipartStream(input,
+ *                                                              boundary);
+ *        boolean nextPart = malitPartStream.skipPreamble();
+ *        OutputStream output;
+ *        while(nextPart) {
+ *            header = chunks.readHeader();
+ *            // process headers
+ *            // create some output stream
+ *            multipartStream.readBodyPart(output);
+ *            nextPart = multipartStream.readBoundary();
+ *        }
+ *    } catch(MultipartStream.MalformedStreamException e) {
+ *          // the stream failed to follow required syntax
+ *    } catch(IOException) {
+ *          // a read or write error occurred
+ *    }
+ *
+ * </pre>
+ *
+ * @author <a href="mailto:[EMAIL PROTECTED]">Rafal Krzewski</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Martin Cooper</a>
+ * @author Sean C. Sullivan
+ *
+ * @version $Id: MultipartStream.java,v 1.12 2003/06/01 00:18:13 martinc Exp $
+ */
+public class MultipartStream
+{
+
+    // ----------------------------------------------------- Manifest constants
+
+
+    /**
+     * The maximum length of <code>header-part</code> that will be
+     * processed (10 kilobytes = 10240 bytes.).
+     */
+    public static final int HEADER_PART_SIZE_MAX = 10240;
+
+
+    /**
+     * The default length of the buffer used for processing a request.
+     */
+    protected static final int DEFAULT_BUFSIZE = 4096;
+
+
+    /**
+     * A byte sequence that marks the end of <code>header-part</code>
+     * (<code>CRLFCRLF</code>).
+     */
+    protected static final byte[] HEADER_SEPARATOR = {0x0D, 0x0A, 0x0D, 0x0A};
+
+
+    /**
+     * A byte sequence that that follows a delimiter that will be
+     * followed by an encapsulation (<code>CRLF</code>).
+     */
+    protected static final byte[] FIELD_SEPARATOR = { 0x0D, 0x0A };
+
+
+    /**
+     * A byte sequence that that follows a delimiter of the last
+     * encapsulation in the stream (<code>--</code>).
+     */
+    protected static final byte[] STREAM_TERMINATOR = { 0x2D, 0x2D };
+
+
+    // ----------------------------------------------------------- Data members
+
+
+    /**
+     * The input stream from which data is read.
+     */
+    private InputStream input;
+
+
+    /**
+     * The length of the boundary token plus the leading <code>CRLF--</code>.
+     */
+    private int boundaryLength;
+
+
+    /**
+     * The amount of data, in bytes, that must be kept in the buffer in order
+     * to detect delimiters reliably.
+     */
+    private int keepRegion;
+
+
+    /**
+     * The byte sequence that partitions the stream.
+     */
+    private byte[] boundary;
+
+
+    /**
+     * The length of the buffer used for processing the request.
+     */
+    private int bufSize;
+
+
+    /**
+     * The buffer used for processing the request.
+     */
+    private byte[] buffer;
+
+
+    /**
+     * The index of first valid character in the buffer.
+     * <br>
+     * 0 <= head < bufSize
+     */
+    private int head;
+
+
+    /**
+     * The index of last valid characer in the buffer + 1.
+     * <br>
+     * 0 <= tail <= bufSize
+     */
+    private int tail;
+
+
+    /**
+     * The content encoding to use when reading headers.
+     */
+    private String headerEncoding;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     *
+     * @see #MultipartStream(InputStream, byte[], int)
+     * @see #MultipartStream(InputStream, byte[])
+     *
+     */
+    public MultipartStream()
+    {
+    }
+
+
+    /**
+     * <p> Constructs a <code>MultipartStream</code> with a custom size buffer.
+     *
+     * <p> Note that the buffer must be at least big enough to contain the
+     * boundary string, plus 4 characters for CR/LF and double dash, plus at
+     * least one byte of data.  Too small a buffer size setting will degrade
+     * performance.
+     *
+     * @param input    The <code>InputStream</code> to serve as a data source.
+     * @param boundary The token used for dividing the stream into
+     *                 <code>encapsulations</code>.
+     * @param bufSize  The size of the buffer to be used, in bytes.
+     *
+     *
+     * @see #MultipartStream()
+     * @see #MultipartStream(InputStream, byte[])
+     *
+     */
+    public MultipartStream(InputStream input,
+                           byte[] boundary,
+                           int bufSize)
+    {
+        this.input = input;
+        this.bufSize = bufSize;
+        this.buffer = new byte[bufSize];
+
+        // We prepend CR/LF to the boundary to chop trailng CR/LF from
+        // body-data tokens.
+        this.boundary = new byte[boundary.length + 4];
+        this.boundaryLength = boundary.length + 4;
+        this.keepRegion = boundary.length + 3;
+        this.boundary[0] = 0x0D;
+        this.boundary[1] = 0x0A;
+        this.boundary[2] = 0x2D;
+        this.boundary[3] = 0x2D;
+        System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);
+
+        head = 0;
+        tail = 0;
+    }
+
+
+    /**
+     * <p> Constructs a <code>MultipartStream</code> with a default size 
buffer.
+     *
+     * @param input    The <code>InputStream</code> to serve as a data source.
+     * @param boundary The token used for dividing the stream into
+     *                 <code>encapsulations</code>.
+     *
+     * @exception IOException when an error occurs.
+     *
+     * @see #MultipartStream()
+     * @see #MultipartStream(InputStream, byte[], int)
+     *
+     */
+    public MultipartStream(InputStream input,
+                           byte[] boundary)
+        throws IOException
+    {
+        this(input, boundary, DEFAULT_BUFSIZE);
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Retrieves the character encoding used when reading the headers of an
+     * individual part. When not specified, or <code>null</code>, the platform
+     * default encoding is used.
+
+     *
+     * @return The encoding used to read part headers.
+     */
+    public String getHeaderEncoding()
+    {
+        return headerEncoding;
+    }
+
+
+    /**
+     * Specifies the character encoding to be used when reading the headers of
+     * individual parts. When not specified, or <code>null</code>, the platform
+     * default encoding is used.
+     *
+     * @param encoding The encoding used to read part headers.
+     */
+    public void setHeaderEncoding(String encoding)
+    {
+        headerEncoding = encoding;
+    }
+
+
+    /**
+     * Reads a byte from the <code>buffer</code>, and refills it as
+     * necessary.
+     *
+     * @return The next byte from the input stream.
+     *
+     * @exception IOException if there is no more data available.
+     */
+    public byte readByte()
+        throws IOException
+    {
+        // Buffer depleted ?
+        if (head == tail)
+        {
+            head = 0;
+            // Refill.
+            tail = input.read(buffer, head, bufSize);
+            if (tail == -1)
+            {
+                // No more data available.
+                throw new IOException("No more data is available");
+            }
+        }
+        return buffer[head++];
+    }
+
+
+    /**
+     * Skips a <code>boundary</code> token, and checks whether more
+     * <code>encapsulations</code> are contained in the stream.
+     *
+     * @return <code>true</code> if there are more encapsulations in
+     *         this stream; <code>false</code> otherwise.
+     *
+     * @exception MalformedStreamException if the stream ends unexpecetedly or
+     *                                     fails to follow required syntax.
+     */
+    public boolean readBoundary()
+        throws MalformedStreamException
+    {
+        byte[] marker = new byte[2];
+        boolean nextChunk = false;
+
+        head += boundaryLength;
+        try
+        {
+            marker[0] = readByte();
+            marker[1] = readByte();
+            if (arrayequals(marker, STREAM_TERMINATOR, 2))
+            {
+                nextChunk = false;
+            }
+            else if (arrayequals(marker, FIELD_SEPARATOR, 2))
+            {
+                nextChunk = true;
+            }
+            else
+            {
+                throw new MalformedStreamException(
+                        "Unexpected characters follow a boundary");
+            }
+        }
+        catch (IOException e)
+        {
+            throw new MalformedStreamException("Stream ended unexpectedly");
+        }
+        return nextChunk;
+    }
+
+
+    /**
+     * <p>Changes the boundary token used for partitioning the stream.
+     *
+     * <p>This method allows single pass processing of nested multipart
+     * streams.
+     *
+     * <p>The boundary token of the nested stream is <code>required</code>
+     * to be of the same length as the boundary token in parent stream.
+     *
+     * <p>Restoring the parent stream boundary token after processing of a
+     * nested stream is left to the application.
+     *
+     * @param boundary The boundary to be used for parsing of the nested
+     *                 stream.
+     *
+     * @exception IllegalBoundaryException if the <code>boundary</code>
+     *                                     has a different length than the one
+     *                                     being currently parsed.
+     */
+    public void setBoundary(byte[] boundary)
+        throws IllegalBoundaryException
+    {
+        if (boundary.length != boundaryLength - 4)
+        {
+            throw new IllegalBoundaryException(
+                    "The length of a boundary token can not be changed");
+        }
+        System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);
+    }
+
+
+    /**
+     * <p>Reads the <code>header-part</code> of the current
+     * <code>encapsulation</code>.
+     *
+     * <p>Headers are returned verbatim to the input stream, including the
+     * trailing <code>CRLF</code> marker. Parsing is left to the
+     * application.
+     *
+     * <p><strong>TODO</strong> allow limiting maximum header size to
+     * protect against abuse.
+     *
+     * @return The <code>header-part</code> of the current encapsulation.
+     *
+     * @exception MalformedStreamException if the stream ends unexpecetedly.
+     */
+    public String readHeaders()
+        throws MalformedStreamException
+    {
+        int i = 0;
+        byte b[] = new byte[1];
+        // to support multi-byte characters
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        int sizeMax = HEADER_PART_SIZE_MAX;
+        int size = 0;
+        while (i < 4)
+        {
+            try
+            {
+                b[0] = readByte();
+            }
+            catch (IOException e)
+            {
+                throw new MalformedStreamException("Stream ended 
unexpectedly");
+            }
+            size++;
+            if (b[0] == HEADER_SEPARATOR[i])
+            {
+                i++;
+            }
+            else
+            {
+                i = 0;
+            }
+            if (size <= sizeMax)
+            {
+                baos.write(b[0]);
+            }
+        }
+
+        String headers = null;
+        if (headerEncoding != null)
+        {
+            try
+            {
+                headers = baos.toString(headerEncoding);
+            }
+            catch (UnsupportedEncodingException e)
+            {
+                // Fall back to platform default if specified encoding is not
+                // supported.
+                headers = baos.toString();
+            }
+        }
+        else
+        {
+            headers = baos.toString();
+        }
+
+        return headers;
+    }
+
+
+    /**
+     * <p>Reads <code>body-data</code> from the current
+     * <code>encapsulation</code> and writes its contents into the
+     * output <code>Stream</code>.
+     *
+     * <p>Arbitrary large amounts of data can be processed by this
+     * method using a constant size buffer. (see [EMAIL PROTECTED]
+     * #MultipartStream(InputStream,byte[],int) constructor}).
+     *
+     * @param output The <code>Stream</code> to write data into.
+     *
+     * @return the amount of data written.
+     *
+     * @exception MalformedStreamException if the stream ends unexpectedly.
+     * @exception IOException              if an i/o error occurs.
+     */
+    public int readBodyData(OutputStream output)
+        throws MalformedStreamException,
+               IOException
+    {
+        boolean done = false;
+        int pad;
+        int pos;
+        int bytesRead;
+        int total = 0;
+        while (!done)
+        {
+            // Is boundary token present somewere in the buffer?
+            pos = findSeparator();
+            if (pos != -1)
+            {
+                // Write the rest of the data before the boundary.
+                output.write(buffer, head, pos - head);
+                total += pos - head;
+                head = pos;
+                done = true;
+            }
+            else
+            {
+                // Determine how much data should be kept in the
+                // buffer.
+                if (tail - head > keepRegion)
+                {
+                    pad = keepRegion;
+                }
+                else
+                {
+                    pad = tail - head;
+                }
+                // Write out the data belonging to the body-data.
+                output.write(buffer, head, tail - head - pad);
+
+                // Move the data to the beging of the buffer.
+                total += tail - head - pad;
+                System.arraycopy(buffer, tail - pad, buffer, 0, pad);
+
+                // Refill buffer with new data.
+                head = 0;
+                bytesRead = input.read(buffer, pad, bufSize - pad);
+
+                // [pprrrrrrr]
+                if (bytesRead != -1)
+                {
+                    tail = pad + bytesRead;
+                }
+                else
+                {
+                    // The last pad amount is left in the buffer.
+                    // Boundary can't be in there so write out the
+                    // data you have and signal an error condition.
+                    output.write(buffer, 0, pad);
+                    output.flush();
+                    total += pad;
+                    throw new MalformedStreamException(
+                            "Stream ended unexpectedly");
+                }
+            }
+        }
+        output.flush();
+        return total;
+    }
+
+
+    /**
+     * <p> Reads <code>body-data</code> from the current
+     * <code>encapsulation</code> and discards it.
+     *
+     * <p>Use this method to skip encapsulations you don't need or don't
+     * understand.
+     *
+     * @return The amount of data discarded.
+     *
+     * @exception MalformedStreamException if the stream ends unexpectedly.
+     * @exception IOException              if an i/o error occurs.
+     */
+    public int discardBodyData()
+        throws MalformedStreamException,
+               IOException
+    {
+        boolean done = false;
+        int pad;
+        int pos;
+        int bytesRead;
+        int total = 0;
+        while (!done)
+        {
+            // Is boundary token present somewere in the buffer?
+            pos = findSeparator();
+            if (pos != -1)
+            {
+                // Write the rest of the data before the boundary.
+                total += pos - head;
+                head = pos;
+                done = true;
+            }
+            else
+            {
+                // Determine how much data should be kept in the
+                // buffer.
+                if (tail - head > keepRegion)
+                {
+                    pad = keepRegion;
+                }
+                else
+                {
+                    pad = tail - head;
+                }
+                total += tail - head - pad;
+
+                // Move the data to the beging of the buffer.
+                System.arraycopy(buffer, tail - pad, buffer, 0, pad);
+
+                // Refill buffer with new data.
+                head = 0;
+                bytesRead = input.read(buffer, pad, bufSize - pad);
+
+                // [pprrrrrrr]
+                if (bytesRead != -1)
+                {
+                    tail = pad + bytesRead;
+                }
+                else
+                {
+                    // The last pad amount is left in the buffer.
+                    // Boundary can't be in there so signal an error
+                    // condition.
+                    total += pad;
+                    throw new MalformedStreamException(
+                            "Stream ended unexpectedly");
+                }
+            }
+        }
+        return total;
+    }
+
+
+    /**
+     * Finds the beginning of the first <code>encapsulation</code>.
+     *
+     * @return <code>true</code> if an <code>encapsulation</code> was found in
+     *         the stream.
+     *
+     * @exception IOException if an i/o error occurs.
+     */
+    public boolean skipPreamble()
+        throws IOException
+    {
+        // First delimiter may be not preceeded with a CRLF.
+        System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
+        boundaryLength = boundary.length - 2;
+        try
+        {
+            // Discard all data up to the delimiter.
+            discardBodyData();
+
+            // Read boundary - if succeded, the stream contains an
+            // encapsulation.
+            return readBoundary();
+        }
+        catch (MalformedStreamException e)
+        {
+            return false;
+        }
+        finally
+        {
+            // Restore delimiter.
+            System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
+            boundaryLength = boundary.length;
+            boundary[0] = 0x0D;
+            boundary[1] = 0x0A;
+        }
+    }
+
+
+    /**
+     * Compares <code>count</code> first bytes in the arrays
+     * <code>a</code> and <code>b</code>.
+     *
+     * @param a     The first array to compare.
+     * @param b     The second array to compare.
+     * @param count How many bytes should be compared.
+     *
+     * @return <code>true</code> if <code>count</code> first bytes in arrays
+     *         <code>a</code> and <code>b</code> are equal.
+     */
+    public static boolean arrayequals(byte[] a,
+                                      byte[] b,
+                                      int count)
+    {
+        for (int i = 0; i < count; i++)
+        {
+            if (a[i] != b[i])
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Searches for a byte of specified value in the <code>buffer</code>,
+     * starting at the specified <code>position</code>.
+     *
+     * @param value The value to find.
+     * @param pos   The starting position for searching.
+     *
+     * @return The position of byte found, counting from beginning of the
+     *         <code>buffer</code>, or <code>-1</code> if not found.
+     */
+    protected int findByte(byte value,
+                           int pos)
+    {
+        for (int i = pos; i < tail; i++)
+        {
+            if (buffer[i] == value)
+            {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+
+    /**
+     * Searches for the <code>boundary</code> in the <code>buffer</code>
+     * region delimited by <code>head</code> and <code>tail</code>.
+     *
+     * @return The position of the boundary found, counting from the
+     *         beginning of the <code>buffer</code>, or <code>-1</code> if
+     *         not found.
+     */
+    protected int findSeparator()
+    {
+        int first;
+        int match = 0;
+        int maxpos = tail - boundaryLength;
+        for (first = head;
+             (first <= maxpos) && (match != boundaryLength);
+             first++)
+        {
+            first = findByte(boundary[0], first);
+            if (first == -1 || (first > maxpos))
+            {
+                return -1;
+            }
+            for (match = 1; match < boundaryLength; match++)
+            {
+                if (buffer[first + match] != boundary[match])
+                {
+                    break;
+                }
+            }
+        }
+        if (match == boundaryLength)
+        {
+            return first - 1;
+        }
+        return -1;
+    }
+
+    /**
+     * Returns a string representation of this object.
+     *
+     * @return The string representation of this object.
+     */
+    public String toString()
+    {
+        StringBuffer sbTemp = new StringBuffer();
+        sbTemp.append("boundary='");
+        sbTemp.append(String.valueOf(boundary));
+        sbTemp.append("'\nbufSize=");
+        sbTemp.append(bufSize);
+        return sbTemp.toString();
+    }
+
+    /**
+     * Thrown to indicate that the input stream fails to follow the
+     * required syntax.
+     */
+    public class MalformedStreamException
+        extends IOException
+    {
+        /**
+         * Constructs a <code>MalformedStreamException</code> with no
+         * detail message.
+         */
+        public MalformedStreamException()
+        {
+            super();
+        }
+
+        /**
+         * Constructs an <code>MalformedStreamException</code> with
+         * the specified detail message.
+         *
+         * @param message The detail message.
+         */
+        public MalformedStreamException(String message)
+        {
+            super(message);
+        }
+    }
+
+
+    /**
+     * Thrown upon attempt of setting an invalid boundary token.
+     */
+    public class IllegalBoundaryException
+        extends IOException
+    {
+        /**
+         * Constructs an <code>IllegalBoundaryException</code> with no
+         * detail message.
+         */
+        public IllegalBoundaryException()
+        {
+            super();
+        }
+
+        /**
+         * Constructs an <code>IllegalBoundaryException</code> with
+         * the specified detail message.
+         *
+         * @param message The detail message.
+         */
+        public IllegalBoundaryException(String message)
+        {
+            super(message);
+        }
+    }
+
+
+    // ------------------------------------------------------ Debugging methods
+
+
+    // These are the methods that were used to debug this stuff.
+    /*
+
+    // Dump data.
+    protected void dump()
+    {
+        System.out.println("01234567890");
+        byte[] temp = new byte[buffer.length];
+        for(int i=0; i<buffer.length; i++)
+        {
+            if (buffer[i] == 0x0D || buffer[i] == 0x0A)
+            {
+                temp[i] = 0x21;
+            }
+            else
+            {
+                temp[i] = buffer[i];
+            }
+        }
+        System.out.println(new String(temp));
+        int i;
+        for (i=0; i<head; i++)
+            System.out.print(" ");
+        System.out.println("h");
+        for (i=0; i<tail; i++)
+            System.out.print(" ");
+        System.out.println("t");
+        System.out.flush();
+    }
+
+    // Main routine, for testing purposes only.
+    //
+    // @param args A String[] with the command line arguments.
+    // @exception Exception, a generic exception.
+    public static void main( String[] args )
+        throws Exception
+    {
+        File boundaryFile = new File("boundary.dat");
+        int boundarySize = (int)boundaryFile.length();
+        byte[] boundary = new byte[boundarySize];
+        FileInputStream input = new FileInputStream(boundaryFile);
+        input.read(boundary,0,boundarySize);
+
+        input = new FileInputStream("multipart.dat");
+        MultipartStream chunks = new MultipartStream(input, boundary);
+
+        int i = 0;
+        String header;
+        OutputStream output;
+        boolean nextChunk = chunks.skipPreamble();
+        while (nextChunk)
+        {
+            header = chunks.readHeaders();
+            System.out.println("!"+header+"!");
+            System.out.println("wrote part"+i+".dat");
+            output = new FileOutputStream("part"+(i++)+".dat");
+            chunks.readBodyData(output);
+            nextChunk = chunks.readBoundary();
+        }
+    }
+
+    */
+}

Propchange: 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java?rev=412780&view=auto
==============================================================================
--- 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java
 (added)
+++ 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java
 Thu Jun  8 08:35:56 2006
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2001-2006 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tomcat.util.http.fileupload;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * An output stream which triggers an event when a specified number of bytes of
+ * data have been written to it. The event can be used, for example, to throw
+ * an exception if a maximum has been reached, or to switch the underlying
+ * stream type when the threshold is exceeded.
+ * <p>
+ * This class overrides all <code>OutputStream</code> methods. However, these
+ * overrides ultimately call the corresponding methods in the underlying output
+ * stream implementation.
+ * <p>
+ * NOTE: This implementation may trigger the event <em>before</em> the 
threshold
+ * is actually reached, since it triggers when a pending write operation would
+ * cause the threshold to be exceeded.
+ *
+ * @author <a href="mailto:[EMAIL PROTECTED]">Martin Cooper</a>
+ *
+ * @version $Id: ThresholdingOutputStream.java,v 1.3 2003/05/31 22:31:08 
martinc Exp $
+ */
+public abstract class ThresholdingOutputStream
+    extends OutputStream
+{
+
+    // ----------------------------------------------------------- Data members
+
+
+    /**
+     * The threshold at which the event will be triggered.
+     */
+    private int threshold;
+
+
+    /**
+     * The number of bytes written to the output stream.
+     */
+    private long written;
+
+
+    /**
+     * Whether or not the configured threshold has been exceeded.
+     */
+    private boolean thresholdExceeded;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructs an instance of this class which will trigger an event at the
+     * specified threshold.
+     *
+     * @param threshold The number of bytes at which to trigger an event.
+     */
+    public ThresholdingOutputStream(int threshold)
+    {
+        this.threshold = threshold;
+    }
+
+
+    // --------------------------------------------------- OutputStream methods
+
+
+    /**
+     * Writes the specified byte to this output stream.
+     *
+     * @param b The byte to be written.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(int b) throws IOException
+    {
+        checkThreshold(1);
+        getStream().write(b);
+        written++;
+    }
+
+
+    /**
+     * Writes <code>b.length</code> bytes from the specified byte array to this
+     * output stream.
+     *
+     * @param b The array of bytes to be written.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(byte b[]) throws IOException
+    {
+        checkThreshold(b.length);
+        getStream().write(b);
+        written += b.length;
+    }
+
+
+    /**
+     * Writes <code>len</code> bytes from the specified byte array starting at
+     * offset <code>off</code> to this output stream.
+     *
+     * @param b   The byte array from which the data will be written.
+     * @param off The start offset in the byte array.
+     * @param len The number of bytes to write.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void write(byte b[], int off, int len) throws IOException
+    {
+        checkThreshold(len);
+        getStream().write(b, off, len);
+        written += len;
+    }
+
+
+    /**
+     * Flushes this output stream and forces any buffered output bytes to be
+     * written out.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void flush() throws IOException
+    {
+        getStream().flush();
+    }
+
+
+    /**
+     * Closes this output stream and releases any system resources associated
+     * with this stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    public void close() throws IOException
+    {
+        try
+        {
+            flush();
+        }
+        catch (IOException ignored)
+        {
+            // ignore
+        }
+        getStream().close();
+    }
+
+
+    // --------------------------------------------------------- Public methods
+
+
+    /**
+     * Returns the threshold, in bytes, at which an event will be triggered.
+     *
+     * @return The threshold point, in bytes.
+     */
+    public int getThreshold()
+    {
+        return threshold;
+    }
+
+
+    /**
+     * Returns the number of bytes that have been written to this output 
stream.
+     *
+     * @return The number of bytes written.
+     */
+    public long getByteCount()
+    {
+        return written;
+    }
+
+
+    /**
+     * Determines whether or not the configured threshold has been exceeded for
+     * this output stream.
+     *
+     * @return <code>true</code> if the threshold has been reached;
+     *         <code>false</code> otherwise.
+     */
+    public boolean isThresholdExceeded()
+    {
+        return (written > threshold);
+    }
+
+
+    // ------------------------------------------------------ Protected methods
+
+
+    /**
+     * Checks to see if writing the specified number of bytes would cause the
+     * configured threshold to be exceeded. If so, triggers an event to allow
+     * a concrete implementation to take action on this.
+     *
+     * @param count The number of bytes about to be written to the underlying
+     *              output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected void checkThreshold(int count) throws IOException
+    {
+        if (!thresholdExceeded && (written + count > threshold))
+        {
+            thresholdReached();
+            thresholdExceeded = true;
+        }
+    }
+
+
+    // ------------------------------------------------------- Abstract methods
+
+
+    /**
+     * Returns the underlying output stream, to which the corresponding
+     * <code>OutputStream</code> methods in this class will ultimately 
delegate.
+     *
+     * @return The underlying output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected abstract OutputStream getStream() throws IOException;
+
+
+    /**
+     * Indicates that the configured threshold has been reached, and that a
+     * subclass should take whatever action necessary on this event. This may
+     * include changing the underlying output stream.
+     *
+     * @exception IOException if an error occurs.
+     */
+    protected abstract void thresholdReached() throws IOException;
+}

Propchange: 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/package.html
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/package.html?rev=412780&view=auto
==============================================================================
--- 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/package.html 
(added)
+++ 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/package.html 
Thu Jun  8 08:35:56 2006
@@ -0,0 +1,66 @@
+<!-- $Id: package.html,v 1.3 2003/04/27 17:30:06 martinc Exp $ -->
+<html>
+   <head>
+      <title>Overview of the org.apache.commons.fileupload component</title>
+   </head>
+   <body>
+      <p>
+         Component for handling html file uploads as given by rfc 1867
+         <a href="http://www.ietf.org/rfc/rfc1867.txt";>RFC&nbsp;1867</a>.
+      </p>
+      <p>
+         Normal usage of the package involves
+         [EMAIL PROTECTED] org.apache.commons.fileupload.DiskFileUpload 
DiskFileUpload}
+         parsing the HttpServletRequest and returning a list of
+         [EMAIL PROTECTED] org.apache.commons.fileupload.FileItem FileItem}'s.
+         These <code>FileItem</code>'s provide easy access to the data
+         given in the upload.  There is also a low level api for
+         manipulating the upload data encapsulated in the
+         [EMAIL PROTECTED] org.apache.commons.fileupload.MultipartStream 
MultipartStream}
+         class.
+      </p>
+
+      <p>
+         Normal usage example:
+      </p>
+<pre>
+
+    public void doPost(HttpServletRequest req, HttpServletResponse res)
+    {
+        DiskFileUpload fu = new DiskFileUpload();
+        // maximum size before a FileUploadException will be thrown
+        fu.setSizeMax(1000000);
+        // maximum size that will be stored in memory
+        fu.setSizeThreshold(4096);
+        // the location for saving data that is larger than getSizeThreshold()
+        fu.setRepositoryPath("/tmp");
+
+        List fileItems = fu.parseRequest(req);
+        // assume we know there are two files. The first file is a small
+        // text file, the second is unknown and is written to a file on
+        // the server
+        Iterator i = fileItems.iterator();
+        String comment = ((FileItem)i.next()).getString();
+        FileItem fi = (FileItem)i.next();
+        // filename on the client
+        String fileName = fi.getName();
+        // save comment and filename to database
+        ...
+        // write the file
+        fi.write("/www/uploads/" + fileName);
+    }
+</pre>
+      <p>
+         In the example above the first file is loaded into memory as a
+         <code>String</code>. Before calling the getString method, the data
+         may have been in memory or on disk depending on its size.  The second
+         file we assume it will be large and therefore never explicitly load
+         it into memory, though if it is less than 4096 bytes it will be
+         in memory before it is written to its final location.  When writing to
+         the final location, if the data is larger than the
+         threshold, an attempt is made to rename the temporary file to
+         the given location.  If it cannot be renamed, it is streamed to the
+         new location.
+      </p>
+   </body>
+</html>

Propchange: 
tomcat/tc6.0.x/trunk/java/org/apache/tomcat/util/http/fileupload/package.html
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to