Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@www.apache.org Received: (qmail 72528 invoked from network); 2 Aug 2006 20:50:16 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 2 Aug 2006 20:50:16 -0000 Received: (qmail 8115 invoked by uid 500); 2 Aug 2006 20:50:13 -0000 Delivered-To: apmail-jakarta-commons-dev-archive@jakarta.apache.org Received: (qmail 8047 invoked by uid 500); 2 Aug 2006 20:50:13 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 8036 invoked by uid 500); 2 Aug 2006 20:50:13 -0000 Received: (qmail 8033 invoked by uid 99); 2 Aug 2006 20:50:13 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 Aug 2006 13:50:13 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 Aug 2006 13:50:11 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id E905F1A981A; Wed, 2 Aug 2006 13:49:50 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r428141 - in /jakarta/commons/proper/fileupload/trunk: ./ .settings/ src/java/org/apache/commons/fileupload/ src/java/org/apache/commons/fileupload/portlet/ src/java/org/apache/commons/fileupload/servlet/ src/test/org/apache/commons/fileupl... Date: Wed, 02 Aug 2006 20:49:48 -0000 To: commons-cvs@jakarta.apache.org From: jochen@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060802204950.E905F1A981A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: jochen Date: Wed Aug 2 13:49:46 2006 New Revision: 428141 URL: http://svn.apache.org/viewvc?rev=428141&view=rev Log: Merged in the streaming branch. Added: jakarta/commons/proper/fileupload/trunk/.settings/ - copied from r420345, jakarta/commons/proper/fileupload/branches/streaming/.settings/ jakarta/commons/proper/fileupload/trunk/.settings/org.eclipse.jdt.core.prefs - copied unchanged from r420345, jakarta/commons/proper/fileupload/branches/streaming/.settings/org.eclipse.jdt.core.prefs jakarta/commons/proper/fileupload/trunk/.settings/org.eclipse.jdt.ui.prefs - copied unchanged from r420345, jakarta/commons/proper/fileupload/branches/streaming/.settings/org.eclipse.jdt.ui.prefs jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileItemIterator.java - copied unchanged from r420345, jakarta/commons/proper/fileupload/branches/streaming/src/java/org/apache/commons/fileupload/FileItemIterator.java jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileItemStream.java - copied unchanged from r420345, jakarta/commons/proper/fileupload/branches/streaming/src/java/org/apache/commons/fileupload/FileItemStream.java jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/StreamUtil.java - copied unchanged from r420345, jakarta/commons/proper/fileupload/branches/streaming/src/java/org/apache/commons/fileupload/StreamUtil.java jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/StreamingTest.java - copied unchanged from r420345, jakarta/commons/proper/fileupload/branches/streaming/src/test/org/apache/commons/fileupload/StreamingTest.java jakarta/commons/proper/fileupload/trunk/xdocs/streaming.xml - copied unchanged from r420345, jakarta/commons/proper/fileupload/branches/streaming/xdocs/streaming.xml Modified: jakarta/commons/proper/fileupload/trunk/.project jakarta/commons/proper/fileupload/trunk/maven.xml jakarta/commons/proper/fileupload/trunk/project.properties jakarta/commons/proper/fileupload/trunk/project.xml jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUpload.java jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadBase.java jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadException.java jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/MultipartStream.java jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/portlet/PortletFileUpload.java jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/servlet/ServletFileUpload.java jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/DiskFileItemSerializeTest.java jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/MockHttpServletRequest.java jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/TestAll.java jakarta/commons/proper/fileupload/trunk/xdocs/changes.xml jakarta/commons/proper/fileupload/trunk/xdocs/index.xml jakarta/commons/proper/fileupload/trunk/xdocs/navigation.xml jakarta/commons/proper/fileupload/trunk/xdocs/using.xml Modified: jakarta/commons/proper/fileupload/trunk/.project URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/.project?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/.project (original) +++ jakarta/commons/proper/fileupload/trunk/.project Wed Aug 2 13:49:46 2006 @@ -15,4 +15,4 @@ org.eclipse.jdt.core.javanature - \ No newline at end of file + Modified: jakarta/commons/proper/fileupload/trunk/maven.xml URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/maven.xml?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/maven.xml (original) +++ jakarta/commons/proper/fileupload/trunk/maven.xml Wed Aug 2 13:49:46 2006 @@ -14,7 +14,7 @@ limitations under the License. --> - Modified: jakarta/commons/proper/fileupload/trunk/project.properties URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/project.properties?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/project.properties (original) +++ jakarta/commons/proper/fileupload/trunk/project.properties Wed Aug 2 13:49:46 2006 @@ -44,7 +44,7 @@ maven.changelog.type=date #maven.changelog.date=lastRelease -maven.changelog.date=2005-12-24 +maven.changelog.date=2006-06-08 # documentation properties maven.xdoc.date=left Modified: jakarta/commons/proper/fileupload/trunk/project.xml URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/project.xml?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/project.xml (original) +++ jakarta/commons/proper/fileupload/trunk/project.xml Wed Aug 2 13:49:46 2006 @@ -21,7 +21,7 @@ FileUpload commons-fileupload commons-fileupload - 1.1.1 + 1.2-SNAPSHOT 2002 File upload component for Java servlets Modified: jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUpload.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUpload.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUpload.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUpload.java Wed Aug 2 13:49:46 2006 @@ -15,6 +15,8 @@ */ package org.apache.commons.fileupload; +import javax.servlet.http.HttpServletRequest; + /** *

High level API for processing file uploads.

* Modified: jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadBase.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadBase.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadBase.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadBase.java Wed Aug 2 13:49:46 2006 @@ -15,14 +15,16 @@ */ package org.apache.commons.fileupload; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; + import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.servlet.ServletRequestContext; @@ -274,6 +276,24 @@ * * @param ctx The context for the request to be parsed. * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + public FileItemIterator getItemIterator(RequestContext ctx) + throws FileUploadException { + return new FileItemIteratorImpl(ctx); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param ctx The context for the request to be parsed. + * * @return A list of FileItem instances parsed from the * request, in the order that they were transmitted. * @@ -282,110 +302,27 @@ */ public List /* FileItem */ parseRequest(RequestContext ctx) throws FileUploadException { - if (ctx == null) { - throw new NullPointerException("ctx parameter"); - } - - ArrayList items = new ArrayList(); - String contentType = ctx.getContentType(); - - if ((null == contentType) - || (!contentType.toLowerCase().startsWith(MULTIPART))) { - throw new InvalidContentTypeException( - "the request doesn't contain a " - + MULTIPART_FORM_DATA - + " or " - + MULTIPART_MIXED - + " stream, content type header is " - + contentType); - } - int requestSize = ctx.getContentLength(); - - if (requestSize == -1) { - throw new UnknownSizeException( - "the request was rejected because its size is unknown"); - } - - if (sizeMax >= 0 && requestSize > sizeMax) { - throw new SizeLimitExceededException( - "the request was rejected because its size (" + requestSize - + ") exceeds the configured maximum (" + sizeMax + ")", - requestSize, sizeMax); - } - - String charEncoding = headerEncoding; - if (charEncoding == null) { - charEncoding = ctx.getCharacterEncoding(); - } - - try { - byte[] boundary = getBoundary(contentType); - if (boundary == null) { - throw new FileUploadException( - "the request was rejected because " - + "no multipart boundary was found"); - } - - InputStream input = ctx.getInputStream(); - - MultipartStream multi = new MultipartStream(input, boundary); - multi.setHeaderEncoding(charEncoding); - - boolean nextPart = multi.skipPreamble(); - while (nextPart) { - Map headers = parseHeaders(multi.readHeaders()); - String fieldName = getFieldName(headers); - if (fieldName != null) { - String subContentType = getHeader(headers, CONTENT_TYPE); - if (subContentType != null && subContentType - .toLowerCase().startsWith(MULTIPART_MIXED)) { - // Multiple files. - byte[] subBoundary = getBoundary(subContentType); - multi.setBoundary(subBoundary); - boolean nextSubPart = multi.skipPreamble(); - while (nextSubPart) { - headers = parseHeaders(multi.readHeaders()); - if (getFileName(headers) != null) { - FileItem item = - createItem(headers, false); - OutputStream os = item.getOutputStream(); - try { - multi.readBodyData(os); - } finally { - os.close(); - } - items.add(item); - } else { - // Ignore anything but files inside - // multipart/mixed. - multi.discardBodyData(); - } - nextSubPart = multi.readBoundary(); - } - multi.setBoundary(boundary); - } else { - FileItem item = createItem(headers, - getFileName(headers) == null); - OutputStream os = item.getOutputStream(); - try { - multi.readBodyData(os); - } finally { - os.close(); - } - items.add(item); - } - } else { - // Skip this part. - multi.discardBodyData(); - } - nextPart = multi.readBoundary(); + FileItemIterator iter = getItemIterator(ctx); + List items = new ArrayList(); + FileItemFactory fac = getFileItemFactory(); + final byte[] buffer = new byte[8192]; + while (iter.hasNext()) { + FileItemStream item = iter.next(); + FileItem fileItem = fac.createItem(item.getFieldName(), + item.getContentType(), item.isFormField(), + item.getName()); + try { + StreamUtil.copy(item.openStream(), fileItem.getOutputStream(), + true, buffer); + } catch (FileUploadIOException e) { + throw (FileUploadException) e.getCause(); + } catch (IOException e) { + throw new IOFileUploadException( + "Processing of " + MULTIPART_FORM_DATA + + " request failed. " + e.getMessage(), e); } - } catch (IOException e) { - throw new FileUploadException( - "Processing of " + MULTIPART_FORM_DATA - + " request failed. " + e.getMessage()); + items.add(fileItem); } - return items; } @@ -483,28 +420,6 @@ /** - * Creates a new {@link FileItem} instance. - * - * @param headers A Map containing the HTTP request - * headers. - * @param isFormField Whether or not this item is a form field, as - * opposed to a file. - * - * @return A newly created FileItem instance. - * - * @throws FileUploadException if an error occurs. - */ - protected FileItem createItem(Map /* String, String */ headers, - boolean isFormField) - throws FileUploadException { - return getFileItemFactory().createItem(getFieldName(headers), - getHeader(headers, CONTENT_TYPE), - isFormField, - getFileName(headers)); - } - - - /** *

Parses the header-part and returns as key/value * pairs. * @@ -578,62 +493,332 @@ return (String) headers.get(name.toLowerCase()); } + /** + * The iterator, which is returned by + * {@link FileUploadBase#getItemIterator(RequestContext)}. + */ + private class FileItemIteratorImpl implements FileItemIterator { + private class FileItemStreamImpl implements FileItemStream { + private final String contentType, fieldName, name; + private final boolean formField; + private final MultipartStream.ItemInputStream stream; + private boolean opened; + + FileItemStreamImpl(String pName, String pFieldName, + String pContentType, boolean pFormField) { + name = pName; + fieldName = pFieldName; + contentType = pContentType; + formField = pFormField; + stream = multi.newInputStream(); + } + + public String getContentType() { + return contentType; + } + + public String getFieldName() { + return fieldName; + } + + public String getName() { + return name; + } + + public boolean isFormField() { + return formField; + } + + public InputStream openStream() throws IOException { + if (opened) { + throw new IllegalStateException("The stream was already opened."); + } + if (stream.isClosed()) { + throw new FileItemStream.ItemSkippedException(); + } + return stream; + } + + void close() throws IOException { + stream.close(); + } + } + + private final MultipartStream multi; + private final byte[] boundary; + private FileItemStreamImpl currentItem; + private String currentFieldName; + private boolean skipPreamble; + private boolean itemValid; + private boolean eof; + + FileItemIteratorImpl(RequestContext ctx) throws FileUploadException { + if (ctx == null) { + throw new NullPointerException("ctx parameter"); + } + + String contentType = ctx.getContentType(); + if ((null == contentType) + || (!contentType.toLowerCase().startsWith(MULTIPART))) { + throw new InvalidContentTypeException( + "the request doesn't contain a " + + MULTIPART_FORM_DATA + + " or " + + MULTIPART_MIXED + + " stream, content type header is " + + contentType); + } + + try { + InputStream input = ctx.getInputStream(); + + if (sizeMax >= 0) { + int requestSize = ctx.getContentLength(); + if (requestSize == -1) { + input = new LimitedInputStream(input, sizeMax); + } else { + if (sizeMax >= 0 && requestSize > sizeMax) { + throw new SizeLimitExceededException( + "the request was rejected because its size (" + requestSize + + ") exceeds the configured maximum (" + sizeMax + ")", + requestSize, sizeMax); + } + } + } + + String charEncoding = headerEncoding; + if (charEncoding == null) { + charEncoding = ctx.getCharacterEncoding(); + } + + boundary = getBoundary(contentType); + if (boundary == null) { + throw new FileUploadException( + "the request was rejected because " + + "no multipart boundary was found"); + } + + multi = new MultipartStream(input, boundary); + multi.setHeaderEncoding(charEncoding); + + skipPreamble = true; + findNextItem(); + } catch (FileUploadIOException e) { + throw (FileUploadException) e.getCause(); + } catch (IOException e) { + throw new FileUploadException( + "Processing of " + MULTIPART_FORM_DATA + + " request failed. " + e.getMessage()); + } + } + + private boolean findNextItem() throws IOException { + if (eof) { + return false; + } + if (currentItem != null) { + currentItem.close(); + currentItem = null; + } + for (;;) { + boolean nextPart; + if (skipPreamble) { + nextPart = multi.skipPreamble(); + } else { + nextPart = multi.readBoundary(); + } + if (!nextPart) { + if (currentFieldName == null) { + // Outer multipart terminated -> No more data + eof = true; + return false; + } + // Inner multipart terminated -> Return to parsing the outer + multi.setBoundary(boundary); + currentFieldName = null; + continue; + } + Map headers = parseHeaders(multi.readHeaders()); + if (currentFieldName == null) { + // We're parsing the outer multipart + String fieldName = getFieldName(headers); + if (fieldName != null) { + String subContentType = getHeader(headers, CONTENT_TYPE); + if (subContentType != null && + subContentType.toLowerCase().startsWith(MULTIPART_MIXED)) { + currentFieldName = fieldName; + // Multiple files associated with this field name + byte[] subBoundary = getBoundary(subContentType); + multi.setBoundary(subBoundary); + skipPreamble = true; + continue; + } else { + String fileName = getFileName(headers); + currentItem = new FileItemStreamImpl(fileName, + fieldName, getHeader(headers, CONTENT_TYPE), + fileName == null); + itemValid = true; + return true; + } + } + } else { + String fileName = getFileName(headers); + if (fileName != null) { + currentItem = new FileItemStreamImpl(fileName, + currentFieldName, getHeader(headers, CONTENT_TYPE), + false); + itemValid = true; + return true; + } + } + multi.discardBodyData(); + } + } + + public boolean hasNext() throws FileUploadException { + if (eof) { + return false; + } + if (itemValid) { + return true; + } + try { + return findNextItem(); + } catch (FileUploadIOException e) { + throw (FileUploadException) e.getCause(); + } catch (IOException e) { + throw new FileUploadException( + "Processing of " + MULTIPART_FORM_DATA + + " request failed. " + e.getMessage()); + } + } + + public FileItemStream next() throws FileUploadException { + if (eof || (!itemValid && !hasNext())) { + throw new NoSuchElementException(); + } + itemValid = false; + return currentItem; + } + } /** - * Thrown to indicate that the request is not a multipart request. + * An input stream, which limits its data size. This stream is + * used, if the content length is unknown. */ - public static class InvalidContentTypeException - extends FileUploadException { - /** - * Constructs a InvalidContentTypeException with no - * detail message. - */ - public InvalidContentTypeException() { - super(); + private static class LimitedInputStream extends FilterInputStream { + private long sizeMax; + private long count; + + private void checkLimit() throws IOException { + if (count > sizeMax) { + FileUploadException ex = new SizeLimitExceededException( + "the request was rejected because its size (" + count + + ") exceeds the configured maximum (" + sizeMax + ")", + count, sizeMax); + throw new FileUploadIOException(ex); + } + } + + public int read() throws IOException { + int res = super.read(); + if (res != -1) { + count++; + checkLimit(); + } + return res; + } + + public int read(byte[] b, int off, int len) throws IOException { + int res = super.read(b, off, len); + if (res > 0) { + count += res; + checkLimit(); + } + return res; } + LimitedInputStream(InputStream pIn, long pSizeMax) { + super(pIn); + sizeMax = pSizeMax; + } + } + + /** + * This exception is thrown for hiding an inner + * {@link FileUploadException} in an {@link IOException}. + */ + public static class FileUploadIOException extends IOException { + private static final long serialVersionUID = -7047616958165584154L; + private final FileUploadException cause; + /** - * Constructs an InvalidContentTypeException with - * the specified detail message. - * - * @param message The detail message. + * Creates a FileUploadIOException with the + * given cause. */ - public InvalidContentTypeException(String message) { - super(message); + public FileUploadIOException(FileUploadException pCause) { + // We're not doing super(pCause) cause of 1.3 compatibility. + cause = pCause; } - } + public Throwable getCause() { + return cause; + } + } /** - * Thrown to indicate that the request size is not specified. + * Thrown to indicate that the request is not a multipart request. */ - public static class UnknownSizeException + public static class InvalidContentTypeException extends FileUploadException { + private static final long serialVersionUID = -9073026332015646668L; + /** - * Constructs a UnknownSizeException with no + * Constructs a InvalidContentTypeException with no * detail message. */ - public UnknownSizeException() { + public InvalidContentTypeException() { super(); } /** - * Constructs an UnknownSizeException with + * Constructs an InvalidContentTypeException with * the specified detail message. * * @param message The detail message. */ - public UnknownSizeException(String message) { + public InvalidContentTypeException(String message) { super(message); } } + /** + * Thrown to indicate an IOException. + */ + public static class IOFileUploadException extends FileUploadException { + private static final long serialVersionUID = 1749796615868477269L; + private final IOException cause; + + /** + * Creates a new instance with the given cause. + */ + public IOFileUploadException(String pMsg, IOException pException) { + super(pMsg); + cause = pException; + } + + public Throwable getCause() { + return cause; + } + } /** * Thrown to indicate that the request size exceeds the configured maximum. */ public static class SizeLimitExceededException extends FileUploadException { + private static final long serialVersionUID = -2474893167098052828L; + /** * The actual size of the request. */ Modified: jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadException.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadException.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadException.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/FileUploadException.java Wed Aug 2 13:49:46 2006 @@ -23,7 +23,7 @@ */ public class FileUploadException extends Exception { - + /** * Constructs a new FileUploadException without message. */ @@ -39,4 +39,6 @@ public FileUploadException(final String msg) { super(msg); } + + } Modified: jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/MultipartStream.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/MultipartStream.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/MultipartStream.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/MultipartStream.java Wed Aug 2 13:49:46 2006 @@ -496,24 +496,13 @@ public int readBodyData(OutputStream output) throws MalformedStreamException, IOException { - final ItemInputStream istream = new ItemInputStream(); - final byte[] bytes = new byte[8192]; - for (;;) { - int res = istream.read(bytes); - if (res == -1) { - if (output != null) { - output.flush(); - } - return (int) istream.getBytesRead(); - } - if (res > 0 && output != null) { - if (output != null) { - output.write(bytes, 0, res); - } - } - } + final InputStream istream = newInputStream(); + return (int) StreamUtil.copy(istream, output, false); } + ItemInputStream newInputStream() { + return new ItemInputStream(); + } /** *

Reads body-data from the current @@ -712,6 +701,7 @@ public class ItemInputStream extends InputStream { private long total; private int pad, pos; + private boolean closed; ItemInputStream() { findSeparator(); @@ -744,6 +734,9 @@ } public int read() throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } if (available() == 0) { if (makeAvailable() == 0) { return -1; @@ -755,6 +748,9 @@ } public int read(byte[] b, int off, int len) throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } if (len == 0) { return 0; } @@ -773,6 +769,9 @@ } public void close() throws IOException { + if (closed) { + return; + } for (;;) { int av = available(); if (av == 0) { @@ -783,9 +782,13 @@ } skip(av); } + closed = true; } public long skip(long bytes) throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } int av = available(); if (av == 0) { av = makeAvailable(); @@ -820,6 +823,12 @@ tail = pad + bytesRead; findSeparator(); return available(); + } + + /** Returns, whether the stream is closed. + */ + public boolean isClosed() { + return closed; } } Modified: jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/portlet/PortletFileUpload.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/portlet/PortletFileUpload.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/portlet/PortletFileUpload.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/portlet/PortletFileUpload.java Wed Aug 2 13:49:46 2006 @@ -16,8 +16,12 @@ package org.apache.commons.fileupload.portlet; import java.util.List; + import javax.portlet.ActionRequest; +import javax.servlet.http.HttpServletRequest; + import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileUpload; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.FileUploadException; @@ -111,5 +115,22 @@ public List /* FileItem */ parseRequest(ActionRequest request) throws FileUploadException { return parseRequest(new PortletRequestContext(request)); + } + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The portlet request to be parsed. + * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + public FileItemIterator getItemIterator(ActionRequest request) throws FileUploadException { + return super.getItemIterator(new PortletRequestContext(request)); } } Modified: jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/servlet/ServletFileUpload.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/servlet/ServletFileUpload.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/servlet/ServletFileUpload.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/java/org/apache/commons/fileupload/servlet/ServletFileUpload.java Wed Aug 2 13:49:46 2006 @@ -16,8 +16,11 @@ package org.apache.commons.fileupload.servlet; import java.util.List; + import javax.servlet.http.HttpServletRequest; + import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileUpload; import org.apache.commons.fileupload.FileUploadException; @@ -114,5 +117,23 @@ public List /* FileItem */ parseRequest(HttpServletRequest request) throws FileUploadException { return parseRequest(new ServletRequestContext(request)); + } + + + /** + * Processes an RFC 1867 + * compliant multipart/form-data stream. + * + * @param request The servlet request to be parsed. + * + * @return An iterator to instances of FileItemStream + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + */ + public FileItemIterator getItemIterator(HttpServletRequest request) throws FileUploadException { + return super.getItemIterator(new ServletRequestContext(request)); } } Modified: jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/DiskFileItemSerializeTest.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/DiskFileItemSerializeTest.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/DiskFileItemSerializeTest.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/DiskFileItemSerializeTest.java Wed Aug 2 13:49:46 2006 @@ -31,7 +31,7 @@ /** * Serialization Unit tests for - * {@link org.apache.commons.fileupload.disk.DiskFileItemTest}. + * {@link org.apache.commons.fileupload.disk.DiskFileItem}. */ public class DiskFileItemSerializeTest extends TestCase { Modified: jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/MockHttpServletRequest.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/MockHttpServletRequest.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/MockHttpServletRequest.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/MockHttpServletRequest.java Wed Aug 2 13:49:46 2006 @@ -18,6 +18,7 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.Principal; import java.util.Enumeration; @@ -41,15 +42,34 @@ class MockHttpServletRequest implements HttpServletRequest { - private byte[] m_requestData; + private final InputStream m_requestData; + private final int length; private String m_strContentType; private Map m_headers = new java.util.HashMap(); + /** + * Creates a new instance with the given request data + * and content type. + */ public MockHttpServletRequest( final byte[] requestData, final String strContentType) { + this(new ByteArrayInputStream(requestData), + requestData.length, strContentType); + } + + /** + * Creates a new instance with the given request data + * and content type. + */ + public MockHttpServletRequest( + final InputStream requestData, + final int requestLength, + final String strContentType) + { m_requestData = requestData; + length = requestLength; m_strContentType = strContentType; m_headers.put(FileUploadBase.CONTENT_TYPE, strContentType); } @@ -302,7 +322,7 @@ } else { - iLength = m_requestData.length; + iLength = length; } return iLength; } @@ -476,16 +496,20 @@ private static class MyServletInputStream extends javax.servlet.ServletInputStream { - private ByteArrayInputStream m_bais; + private final InputStream in; - public MyServletInputStream(byte[] data) + /** + * Creates a new instance, which returns the given + * streams data. + */ + public MyServletInputStream(InputStream pStream) { - m_bais = new ByteArrayInputStream(data); + in = pStream; } - public int read() + public int read() throws IOException { - return m_bais.read(); + return in.read(); } } } Modified: jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/TestAll.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/TestAll.java?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/TestAll.java (original) +++ jakarta/commons/proper/fileupload/trunk/src/test/org/apache/commons/fileupload/TestAll.java Wed Aug 2 13:49:46 2006 @@ -23,20 +23,30 @@ * */ public class TestAll extends TestCase { - + /** + * Creates a new instance. + */ public TestAll(String testName) { super(testName); } + /** + * Runs the test suite (all other test cases). + */ public static Test suite() { TestSuite suite = new TestSuite(); + suite.addTest(new TestSuite(DefaultFileItemTest.class)); + suite.addTest(new TestSuite(DiskFileItemSerializeTest.class)); suite.addTest(new TestSuite(ParameterParserTest.class)); suite.addTest(new TestSuite(MultipartStreamTest.class)); suite.addTest(new TestSuite(ServletFileUploadTest.class)); - suite.addTest(new TestSuite(DefaultFileItemTest.class)); + suite.addTest(new TestSuite(StreamingTest.class)); return suite; } + /** + * Command line interface, which invokes all tests. + */ public static void main(String args[]) { String[] testCaseName = { TestAll.class.getName() }; junit.textui.TestRunner.main(testCaseName); Modified: jakarta/commons/proper/fileupload/trunk/xdocs/changes.xml URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/xdocs/changes.xml?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/xdocs/changes.xml (original) +++ jakarta/commons/proper/fileupload/trunk/xdocs/changes.xml Wed Aug 2 13:49:46 2006 @@ -45,6 +45,12 @@ Eliminated duplicate code. + + Added a streaming API. + + + Eliminated the necessity of a content-length header. + Modified: jakarta/commons/proper/fileupload/trunk/xdocs/index.xml URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/xdocs/index.xml?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/xdocs/index.xml (original) +++ jakarta/commons/proper/fileupload/trunk/xdocs/index.xml Wed Aug 2 13:49:46 2006 @@ -45,6 +45,7 @@ The following documentation is available:

  • User Guide
  • +
  • Streaming API
  • Frequently Asked Questions
  • JavaDoc API
  • Project Reports
  • Modified: jakarta/commons/proper/fileupload/trunk/xdocs/navigation.xml URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/xdocs/navigation.xml?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/xdocs/navigation.xml (original) +++ jakarta/commons/proper/fileupload/trunk/xdocs/navigation.xml Wed Aug 2 13:49:46 2006 @@ -31,6 +31,7 @@ + Modified: jakarta/commons/proper/fileupload/trunk/xdocs/using.xml URL: http://svn.apache.org/viewvc/jakarta/commons/proper/fileupload/trunk/xdocs/using.xml?rev=428141&r1=428140&r2=428141&view=diff ============================================================================== --- jakarta/commons/proper/fileupload/trunk/xdocs/using.xml (original) +++ jakarta/commons/proper/fileupload/trunk/xdocs/using.xml Wed Aug 2 13:49:46 2006 @@ -52,6 +52,12 @@ regardless of its underlying implementation.

    + This page describes the traditional API of the commons fileupload + library. The traditional API is a convenient approach. However, for + ultimate performance, you might prefer the faster + Streaming API. +

    +

    Each file item has a number of properties that might be of interest for your application. For example, every item has a name and a content type, and can provide an InputStream to access its data. On the --------------------------------------------------------------------- To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org For additional commands, e-mail: commons-dev-help@jakarta.apache.org