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