mschachter 02/03/06 11:39:09 Modified: src/share/org/apache/struts/upload DiskMultipartRequestHandler.java MultipartElement.java MultipartIterator.java src/share/org/apache/struts/util RequestUtils.java src/upload/org/apache/struts/webapp/upload UploadAction.java UploadForm.java web/upload upload.jsp web/upload/WEB-INF struts-config.xml web.xml Log: - Extracted/rewrote all multipart parsing logic to MultipartBoundaryInputStream, supplied unit test - Refactored classes accordingly - Bug #2017, Encoding Issues: MultipartIterator now looks for the character encoding first in the content type of the element, then uses HttpServletRequest's getCharacterEncoding if that's not availabe. Otherwise it uses the default encoding. I've been unable to verify whether this works, as I haven't been able to properly set either of the necessary parts to make it work. It will be verified and working by the final release though. - Bug #2757, Redirection not working; still unaddressed - Bug #4170, MaxLengthExceededException: No longer throws exception, error is handled through ActionForm validation and request attributes, example is in struts-upload webapp. But... the webapp example only seems to work in Mozilla 0.9.8 (or does it only work with a persistent connection...) That will be fixed before 1.1 final. There might be some wrinkles, but overall file upload is better off than it was before. Revision Changes Path 1.13 +65 -49 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.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- DiskMultipartRequestHandler.java 26 Feb 2002 03:38:57 -0000 1.12 +++ DiskMultipartRequestHandler.java 6 Mar 2002 19:39:08 -0000 1.13 @@ -66,64 +66,80 @@ * If the request argument is an instance of MultipartRequestWrapper, * the request wrapper will be populated as well. */ - public void handleRequest(HttpServletRequest request) throws ServletException { - - ApplicationConfig appConfig = (ApplicationConfig) - request.getAttribute(Action.APPLICATION_KEY); + public void handleRequest(HttpServletRequest request) throws ServletException + { + ApplicationConfig appConfig = (ApplicationConfig) request.getAttribute(Action.APPLICATION_KEY); retrieveTempDir(appConfig); - - MultipartIterator iterator = - new MultipartIterator(request, - appConfig.getControllerConfig().getBufferSize(), - getMaxSize(appConfig.getControllerConfig().getMaxFileSize()), - tempDir); - MultipartElement element; + try + { + MultipartIterator iterator = new MultipartIterator(request, appConfig.getControllerConfig().getBufferSize(), + getMaxSize(appConfig.getControllerConfig().getMaxFileSize()), + tempDir); + MultipartElement element; - textElements = new Hashtable(); - fileElements = new Hashtable(); - allElements = new Hashtable(); + textElements = new Hashtable(); + fileElements = new Hashtable(); + allElements = new Hashtable(); - try { - while ((element = iterator.getNextElement()) != null) { - if (!element.isFile()) { - - if (request instanceof MultipartRequestWrapper) { - ((MultipartRequestWrapper) request).setParameter(element.getName(), - element.getValue()); - } - String[] textValues = (String[]) textElements.get(element.getName()); - - if (textValues != null) { - String[] textValues2 = new String[textValues.length + 1]; - System.arraycopy(textValues, 0, textValues2, 0, textValues.length); - textValues2[textValues.length] = element.getValue(); - textValues = textValues2; - } - else { - textValues = new String[1]; - textValues[0] = element.getValue(); - } - textElements.put(element.getName(), textValues); - allElements.put(element.getName(), textValues); + while ((element = iterator.getNextElement()) != null) + { + if (!element.isFile()) + { + createTextElement(request, element); } - else { - - 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); - } + else + { + createDiskFile(element); } } + //take care of maximum length being exceeded + if (iterator.isMaxLengthExceeded()) + { + request.setAttribute(MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED, Boolean.TRUE); + } + } + catch (IOException ioe) + { + throw new ServletException(ioe); } - catch (UnsupportedEncodingException uee) { - throw new ServletException("Encoding \"ISO-8859-1\" not supported"); + } + + protected void createTextElement(HttpServletRequest request, MultipartElement element) + { + if (request instanceof MultipartRequestWrapper) + { + ((MultipartRequestWrapper) request).setParameter(element.getName(), element.getValue()); } + String[] textValues = (String[]) textElements.get(element.getName()); + if (textValues != null) + { + String[] textValues2 = new String[textValues.length + 1]; + System.arraycopy(textValues, 0, textValues2, 0, textValues.length); + textValues2[textValues.length] = element.getValue(); + textValues = textValues2; + } + else + { + textValues = new String[1]; + textValues[0] = element.getValue(); + } + textElements.put(element.getName(), textValues); + allElements.put(element.getName(), textValues); + } + + protected void createDiskFile(MultipartElement element) + { + 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); + } } public Hashtable getAllElements() { 1.3 +157 -201 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.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- MultipartElement.java 14 Feb 2001 21:43:06 -0000 1.2 +++ MultipartElement.java 6 Mar 2002 19:39:08 -0000 1.3 @@ -1,201 +1,157 @@ -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 - * whether or not the element is a String or a file, - * and methods to retrieve the data of the aforementioned - * element. Text input elements have a <code>null</code> content type, - * files have a non-null content type. - * - * @author Mike Schachter - */ -public class MultipartElement { - - /** - * The content type of this element - */ - protected String contentType; - - - /** - * 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; - - /** - * The element's filename, null for text elements - */ - 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() { - return contentType; - } - - - /** - * 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() { - return name; - } - - /** - * Retrieve the filename, can return <code>null</code> - * for text elements - */ - public String getFileName() { - 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 - */ - public void setFileName(String fileName) { - this.fileName = fileName; - } - - /** - * Set the name for this element - */ - public void setName(String name) { - this.name = name; - } - - /** - * Set the content type - */ - public void setContentType(String contentType) { - 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; - } -} - +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 * whether or not the element is a String or a file, * and methods to retrieve the data of the aforementioned * element. Text input elements have a <code>null</code> content type, * files have a non-null content type. * * @author Mike Schachter */ +public class MultipartElement +{ + /** * The content type of this element */ + protected String contentType; + + /** * 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; + + /** * The element's filename, null for text elements */ + 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() + { + return contentType; + } + + + /** * 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() + { + return name; + } + + /** * Retrieve the filename, can return <code>null</code> * for text elements */ + public String getFileName() + { + 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 */ + public void setFileName(String fileName) + { + this.fileName = fileName; + } + + + /** * Set the name for this element */ + public void setName(String name) + { + this.name = name; + } + + + /** * Set the content type */ + public void setContentType(String contentType) + { + 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; + } +} \ No newline at end of file 1.19 +197 -358 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.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- MultipartIterator.java 11 Oct 2001 16:28:17 -0000 1.18 +++ MultipartIterator.java 6 Mar 2002 19:39:08 -0000 1.19 @@ -1,11 +1,7 @@ package org.apache.struts.upload; -import java.io.File; -import java.io.IOException; -import java.io.BufferedOutputStream; -import java.io.FileOutputStream; -import java.io.UnsupportedEncodingException; -import javax.servlet.ServletException; +import java.io.*; +import java.util.Iterator; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; @@ -30,47 +26,58 @@ * @see org.apache.struts.upload.MultipartElement * @author Mike Schachter */ -public class MultipartIterator { +public class MultipartIterator +{ /** - * The maximum size in bytes of the buffer used to read lines [4K] + * The default encoding of a text element if none is specified. */ - public static int MAX_LINE_SIZE = 4096; + private static final String DEFAULT_ENCODING = "iso-8859-1"; /** - * The request instance for this class + * The size in bytes to copy of text data at a time. */ - protected HttpServletRequest request; + private static final int TEXT_BUFFER_SIZE = 1000; /** - * The input stream instance for this class + * The name of the Content-Type header. */ - protected BufferedMultipartInputStream inputStream; + public static String HEADER_CONTENT_TYPE = "Content-Type"; /** - * The boundary for this multipart request + * The name of the Content-Disposition header. */ - protected String boundary; + public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition"; /** - * The byte array representing the boundary for this multipart request + * The exception message for when the boundary of a multipart request can't be determined. */ - protected byte[] boundaryBytes; + public static final String MESSAGE_CANNOT_RETRIEVE_BOUNDARY = + "MultipartIterator: cannot retrieve boundary for multipart request"; + + private static final String PARAMETER_BOUNDARY = "boundary="; + + private static final String FILE_PREFIX = "strts"; /** - * Whether or not the input stream is finished + * The request instance for this class */ - protected boolean contentRead = false; + protected HttpServletRequest request; /** - * The maximum file size in bytes allowed. Ignored if -1 + * The InputStream to use to read the multipart data. */ - protected long maxSize = -1; + protected MultipartBoundaryInputStream inputStream; /** - * The total bytes read from this request + * The boundary for this multipart request */ - protected long totalLength = 0; + protected String boundary; + + /** + * The maximum file size in bytes allowed. Ignored if -1 + */ + protected long maxSize = -1; /** * The content length of this request @@ -95,12 +102,23 @@ protected String tempDir; /** + * The content-type. + */ + protected String contentType; + + /** + * Whether the maximum length has been exceeded. + */ + protected boolean maxLengthExceeded; + + /** * Constructs a MultipartIterator with a default buffer size and no file size * limit * * @param request The multipart request to iterate */ - public MultipartIterator(HttpServletRequest request) throws ServletException{ + public MultipartIterator(HttpServletRequest request) throws IOException + { this(request, -1); } @@ -112,8 +130,9 @@ * @param bufferSize The size in bytes that should be read from the input * stream at a times */ - public MultipartIterator(HttpServletRequest request, int bufferSize) throws ServletException { - this (request, bufferSize, -1); + public MultipartIterator(HttpServletRequest request, int bufferSize) throws IOException + { + this (request, bufferSize, -1); } /** @@ -125,34 +144,66 @@ * stream at a times * @param maxSize The maximum size in bytes allowed for a multipart element's data */ - public MultipartIterator(HttpServletRequest request, int bufferSize, long maxSize) - throws ServletException { - + public MultipartIterator(HttpServletRequest request, int bufferSize, long maxSize) throws IOException + { this(request, bufferSize, maxSize, null); - } - public MultipartIterator(HttpServletRequest request, - int bufferSize, - long maxSize, - String tempDir) throws ServletException { - + public MultipartIterator(HttpServletRequest request, int bufferSize, long maxSize, String tempDir) throws IOException + { this.request = request; this.maxSize = maxSize; - if (bufferSize > -1) { + if (bufferSize > -1) + { this.bufferSize = bufferSize; } - if (tempDir != null) { + if (tempDir != null) + { this.tempDir = tempDir; } - else { + else + { //default to system-wide tempdir - tempDir = System.getProperty("java.io.tmpdir"); + this.tempDir = System.getProperty("java.io.tmpdir"); } + this.maxLengthExceeded = false; + this.inputStream = new MultipartBoundaryInputStream(); parseRequest(); } /** + * Handles retrieving the boundary and setting the input stream + */ + protected void parseRequest() throws IOException + { + //get the content-type header, which contains the boundary used for separating multipart elements + getContentTypeOfRequest(); + //get the content-length header, used to prevent denial of service attacks and for detecting + //whether a file size is over the limit before the client sends the file + this.contentLength = this.request.getContentLength(); + //parse the boundary from the content-type header's value + getBoundaryFromContentType(); + //don't let the stream read past the content length + this.inputStream.setMaxLength(this.contentLength+1); + //just stop now if the content length is bigger than the maximum allowed size + if (this.contentLength > this.maxSize) + { + this.maxLengthExceeded = true; + } + else + { + InputStream requestInputStream = this.request.getInputStream(); + //mark the input stream to allow multiple reads + if (requestInputStream.markSupported()) + { + requestInputStream.mark(contentLength+1); + } + this.inputStream.setBoundary(this.boundary); + this.inputStream.setInputStream(requestInputStream); + } + } + + /** * Retrieves the next element in the iterator if one exists. * * @throws a ServletException if the post size exceeds the maximum file size @@ -162,103 +213,80 @@ * representing the next element in the request data * */ - public MultipartElement getNextElement() throws ServletException, UnsupportedEncodingException { - //retrieve the "Content-Disposition" header - //and parse - String disposition = readLine(); - - - if ((disposition != null) && (disposition.startsWith("Content-Disposition"))) { - String name = parseDispositionName(disposition); - String filename = parseDispositionFilename(disposition); - - String contentType = null; - boolean isFile = (filename != null); - - if (isFile) { - filename = new File(filename).getName(); - - //check for windows filenames, - //from linux jdk's the entire filepath - //isn't parsed correctly from File.getName() - int colonIndex = filename.indexOf(":"); - if (colonIndex == -1) { - //check for Window's SMB server file paths - colonIndex = filename.indexOf("\\\\"); + public MultipartElement getNextElement() throws IOException + { + //the MultipartElement to return + MultipartElement element = null; + if (!isMaxLengthExceeded()) + { + if (!this.inputStream.isFinalBoundaryEncountered()) + { + if (this.inputStream.isElementFile()) + { + //attempt to create the multipart element from the collected data + element = createFileMultipartElement(); } - int slashIndex = filename.lastIndexOf("\\"); - - if ((colonIndex > -1) && (slashIndex > -1)) { - //then consider this filename to be a full - //windows filepath, and parse it accordingly - //to retrieve just the file name - filename = filename.substring(slashIndex+1, filename.length()); + //process a text element + else + { + String encoding = getElementEncoding(); + element = createTextMultipartElement(encoding); } - - //get the content type - contentType = readLine(); - contentType = parseContentType(contentType); + this.inputStream.resetForNextBoundary(); } + } + return element; + } - - - //ignore next line (whitespace) (unless it's a file - //without content-type) - if (! ((isFile) && contentType == null)) { - readLine(); - } - - MultipartElement element = null; - - //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) { - ioe.printStackTrace(System.err); - throw new ServletException("IOException while reading file element: " + ioe.getMessage(), ioe); - } + /** + * Get the character encoding used for this current multipart element. + */ + protected String getElementEncoding() + { + String encoding = this.inputStream.getElementCharset(); + if (encoding == null) + { + encoding = this.request.getCharacterEncoding(); + if (encoding == null) + { + encoding = DEFAULT_ENCODING; } - else { - //read data into String form, then convert to bytes - //for text - StringBuffer textData = new StringBuffer(); - String line; - //parse for text data - line = readLine(); - - while ((line != null) && (!line.startsWith(boundary))) { - textData.append(line); - line = readLine(); - } + } + return encoding; + } - if (textData.length() > 0) { - //cut off "\r" from the end if necessary - if (textData.charAt(textData.length()-1) == '\r') { - textData.setLength(textData.length()-1); - } - } + /** + * Create a text element from the data in the body of the element. + * @param encoding The character encoding of the string. + */ + protected MultipartElement createTextMultipartElement(String encoding) throws IOException + { + MultipartElement element; - //create the element - element = new MultipartElement(name, textData.toString()); - } - return element; + int read = 0; + byte[] buffer = new byte[TEXT_BUFFER_SIZE]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while ((read = this.inputStream.read(buffer, 0, TEXT_BUFFER_SIZE)) > 0) + { + baos.write(buffer, 0, read); } + //create the element + String value = baos.toString(encoding); + element = new MultipartElement(this.inputStream.getElementName(), value); + return element; + } - //reset stream - if (inputStream.markSupported()) { - try { - inputStream.reset(); - } - catch (IOException ioe) { - throw new ServletException("IOException while resetting input stream: " + - ioe.getMessage()); - } - } - return null; + /** + * Create a multipart element instance representing the file in the stream. + */ + protected MultipartElement createFileMultipartElement() throws IOException + { + MultipartElement element; + //create a local file on disk representing the element + File elementFile = createLocalFile(); + element = new MultipartElement(this.inputStream.getElementName(), this.inputStream.getElementFileName(), + this.inputStream.getElementContentType(), elementFile); + return element; } /** @@ -292,262 +320,73 @@ * Get the maximum post data size allowed for a multipart request * @return The maximum post data size in bytes */ - public long getMaxSize() { - return maxSize; + public long getMaxSize() + { + return this.maxSize; } /** - * Handles retrieving the boundary and setting the input stream + * Whether or not the maximum length has been exceeded by the client. */ - protected void parseRequest() throws ServletException { - - contentLength = request.getContentLength(); - - //set boundary - boundary = parseBoundary(request.getContentType()); - boundaryBytes = boundary.getBytes(); - - try { - //set the input stream - inputStream = new BufferedMultipartInputStream(request.getInputStream(), - bufferSize, - contentLength, - maxSize); - //mark the input stream to allow multiple reads - if (inputStream.markSupported()) { - inputStream.mark(contentLength+1); - } - - } - catch (IOException ioe) { - throw new ServletException("Problem while reading request: " + - ioe.getMessage(), ioe); - } - - if ((boundary == null) || (boundary.length() < 1)) { - //try retrieving the header through more "normal" means - boundary = parseBoundary(request.getHeader("Content-type")); - } - - if ((boundary == null) || (boundary.length() < 1)) { - throw new ServletException("MultipartIterator: cannot retrieve boundary " + - "for multipart request"); - } - - //read first line - try { - String firstLine = readLine(); - - if (firstLine == null) { - throw new ServletException("MultipartIterator: no multipart request data " + - "sent"); - } - if (!firstLine.startsWith(boundary)) { - throw new ServletException("MultipartIterator: invalid multipart request " + - "data, doesn't start with boundary"); - } - } - catch (UnsupportedEncodingException uee) { - throw new ServletException("MultipartIterator: encoding \"ISO-8859-1\" not supported"); - } + public boolean isMaxLengthExceeded() + { + return (this.maxLengthExceeded || this.inputStream.isMaxLengthMet()); } + /** - * Parses a content-type String for the boundary. Appends a - * "--" to the beginning of the boundary, because thats the - * real boundary as opposed to the shortened one in the - * content type. + * Parses a content-type String for the boundary. */ - public static String parseBoundary(String contentType) { - if (contentType.lastIndexOf("boundary=") != -1) { - String _boundary = "--" + - contentType.substring(contentType.lastIndexOf("boundary=")+9); - if (_boundary.endsWith("\n")) { + private final void getBoundaryFromContentType() throws IOException + { + if (this.contentType.lastIndexOf(PARAMETER_BOUNDARY) != -1) + { + String _boundary = this.contentType.substring(this.contentType.lastIndexOf(PARAMETER_BOUNDARY) + 9); + if (_boundary.endsWith("\n")) + { //strip it off - return _boundary.substring(0, _boundary.length()-1); + this.boundary = _boundary.substring(0, _boundary.length()-1); } - return _boundary; + this.boundary = _boundary; } - return null; - } - - /** - * Parses the "Content-Type" line of a multipart form for a content type - * - * @param contentTypeString A String reprsenting the Content-Type line, - * with a trailing "\n" - * @return The content type specified, or <code>null</code> if one can't be - * found. - */ - public static String parseContentType(String contentTypeString) { - int nameIndex = contentTypeString.indexOf("Content-Type: "); - if (nameIndex == -1) - nameIndex = contentTypeString.indexOf("\n"); - - if (nameIndex != -1) { - int endLineIndex = contentTypeString.indexOf("\n"); - if (endLineIndex == -1) { - endLineIndex = contentTypeString.length()-1; - } - return contentTypeString.substring(nameIndex+14, endLineIndex); + else + { + this.boundary = null; } - return null; - } - - /** - * Retrieves the "name" attribute from a content disposition line - * - * @param dispositionString The entire "Content-disposition" string - * @return <code>null</code> if no name could be found, otherwise, - * returns the name - * @see #parseForAttribute(String, String) - */ - public static String parseDispositionName(String dispositionString) { - return parseForAttribute("name", dispositionString); - } - - /** - * Retrieves the "filename" attribute from a content disposition line - * - * @param dispositionString The entire "Content-disposition" string - * @return <code>null</code> if no filename could be found, otherwise, - * returns the filename - * @see #parseForAttribute(String, String) - */ - public static String parseDispositionFilename(String dispositionString) { - return parseForAttribute("filename", dispositionString); - } - - - /** - * Parses a string looking for a attribute-value pair, and returns the value. - * For example: - * <pre> - * String parseString = "Content-Disposition: filename=\"bob\" name=\"jack\""; - * MultipartIterator.parseForAttribute(parseString, "name"); - * </pre> - * That will return "bob". - * - * @param attribute The name of the attribute you're trying to get - * @param parseString The string to retrieve the value from - * @return The value of the attribute, or <code>null</code> if none could be found - */ - public static String parseForAttribute(String attribute, String parseString) { - int nameIndex = parseString.indexOf(attribute + "=\""); - if (nameIndex != -1) { - - int endQuoteIndex = parseString.indexOf("\"", nameIndex+attribute.length()+3); - - if (endQuoteIndex != -1) { - return parseString.substring(nameIndex+attribute.length()+2, endQuoteIndex); - } - return ""; + //throw an exception if we're unable to obtain a boundary at this point + if ((this.boundary == null) || (this.boundary.length() < 1)) + { + throw new IOException(MESSAGE_CANNOT_RETRIEVE_BOUNDARY); } - return null; } - /** - * Reads the input stream until it reaches a new line + * Gets the value of the Content-Type header of the request. */ - protected String readLine() throws ServletException, UnsupportedEncodingException { - - byte[] bufferByte; - int bytesRead; - - if (totalLength >= contentLength) { - return null; - } - - try { - bufferByte = inputStream.readLine(); - if (bufferByte == null) - return null; - bytesRead = bufferByte.length; - } - catch (IOException ioe) { - throw new ServletException("IOException while reading multipart request: " + - ioe.getMessage()); - } - if (bytesRead == -1) { - return null; + private final void getContentTypeOfRequest() + { + this.contentType = request.getContentType(); + if (this.contentType == null) + { + this.contentType = this.request.getHeader(HEADER_CONTENT_TYPE); } - - totalLength += bytesRead; - return new String(bufferByte, 0, bytesRead, "ISO-8859-1"); } /** - * Creates a file on disk from the current mulitpart element + * Creates a file on disk from the current mulitpart element. * @param fileName the name of the multipart file */ - protected File createLocalFile() throws IOException { - - File tempFile = File.createTempFile("strts", null, new File(tempDir)); - BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile), - diskBufferSize); - byte[] lineBuffer = inputStream.readLine(); - int bytesRead = lineBuffer.length; - - boolean cutCarriage = false; - boolean cutNewline = false; - - try { - while ((bytesRead != -1) && (!equals(lineBuffer, 0, boundaryBytes.length, - boundaryBytes))) { - - if (cutCarriage) { - fos.write('\r'); - } - if (cutNewline) { - fos.write('\n'); - } - cutCarriage = false; - if (bytesRead > 0) { - if (lineBuffer[bytesRead-1] == '\r') { - bytesRead--; - cutCarriage = true; - } - } - cutNewline = true; - fos.write(lineBuffer, 0, bytesRead); - lineBuffer = inputStream.readLine(); - bytesRead = lineBuffer.length; - } - } - catch (IOException ioe) { - fos.close(); - tempFile.delete(); - throw ioe; + protected File createLocalFile() throws IOException + { + File tempFile = File.createTempFile(FILE_PREFIX, null, new File(this.tempDir)); + BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile), this.diskBufferSize); + int read = 0; + byte buffer[] = new byte[this.diskBufferSize]; + while ((read = this.inputStream.read(buffer, 0, this.diskBufferSize)) > 0) + { + fos.write(buffer, 0, read); } - fos.flush(); fos.close(); return tempFile; } - - /** - * Checks bytes for equality. Two byte arrays are equal if - * each of their elements are the same. This method checks - * comp[offset] with source[0] to source[length-1] with - * comp[offset + length - 1] - * @param comp The byte to compare to <code>source</code> - * @param offset The offset to start at in <code>comp</code> - * @param length The length of <code>comp</code> to compare to - * @param source The reference byte array to test for equality - */ - public static boolean equals(byte[] comp, int offset, int length, - byte[] source) { - - if ((length != source.length) || (comp.length - offset < length)) { - return false; - } - - for (int i = 0; i < length; i++) { - if (comp[offset+i] != source[i]) { - return false; - } - } - return true; - } - } 1.31 +15 -12 jakarta-struts/src/share/org/apache/struts/util/RequestUtils.java Index: RequestUtils.java =================================================================== RCS file: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/RequestUtils.java,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- RequestUtils.java 1 Mar 2002 06:24:00 -0000 1.30 +++ RequestUtils.java 6 Mar 2002 19:39:08 -0000 1.31 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/RequestUtils.java,v 1.30 2002/03/01 06:24:00 martinc Exp $ - * $Revision: 1.30 $ - * $Date: 2002/03/01 06:24:00 $ + * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/util/RequestUtils.java,v 1.31 2002/03/06 19:39:08 mschachter Exp $ + * $Revision: 1.31 $ + * $Date: 2002/03/06 19:39:08 $ * * ==================================================================== * @@ -110,7 +110,7 @@ * * @author Craig R. McClanahan * @author Ted Husted - * @version $Revision: 1.30 $ $Date: 2002/03/01 06:24:00 $ + * @version $Revision: 1.31 $ $Date: 2002/03/06 19:39:08 $ */ public class RequestUtils { @@ -840,8 +840,7 @@ } // Obtain a MultipartRequestHandler - MultipartRequestHandler multipartHandler = - getMultipartHandler(request, servlet); + MultipartRequestHandler multipartHandler = getMultipartHandler(request, servlet); // Set the multipart request handler for our ActionForm. // If the bean isn't an ActionForm, an exception would have been @@ -849,17 +848,21 @@ // in fact an ActionForm. ((ActionForm) bean).setMultipartRequestHandler(multipartHandler); - if (multipartHandler != null) { + if (multipartHandler != null) + { isMultipart = true; - // Set servlet and mapping info servlet.setServletFor(multipartHandler); - multipartHandler.setMapping((ActionMapping) - request.getAttribute(Action.MAPPING_KEY)); - + multipartHandler.setMapping((ActionMapping) request.getAttribute(Action.MAPPING_KEY)); // Initialize multipart request class handler multipartHandler.handleRequest(request); - + //stop here if the maximum length has been exceeded + Boolean maxLengthExceeded = (Boolean) + request.getAttribute(MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED); + if ((maxLengthExceeded != null) && (maxLengthExceeded.booleanValue())) + { + return; + } //retrive form values and put into properties multipartElements = multipartHandler.getAllElements(); names = multipartElements.keys(); 1.5 +152 -147 jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadAction.java Index: UploadAction.java =================================================================== RCS file: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadAction.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- UploadAction.java 18 Apr 2001 14:21:23 -0000 1.4 +++ UploadAction.java 6 Mar 2002 19:39:08 -0000 1.5 @@ -1,63 +1,63 @@ /* - * $Header: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadAction.java,v 1.4 2001/04/18 14:21:23 mschachter Exp $ - * $Revision: 1.4 $ - * $Date: 2001/04/18 14:21:23 $ - * - * ==================================================================== - * - * The Apache Software License, Version 1.1 - * - * Copyright (c) 1999-2001 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowlegement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowlegement may appear in the software itself, - * if and wherever such third-party acknowlegements normally appear. - * - * 4. The names "The Jakarta Project", "Struts", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact [EMAIL PROTECTED] - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Group. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ +* $Header: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadAction.java,v 1.5 2002/03/06 19:39:08 mschachter Exp $ +* $Revision: 1.5 $ +* $Date: 2002/03/06 19:39:08 $ +* +* ==================================================================== +* +* The Apache Software License, Version 1.1 +* +* Copyright (c) 1999-2001 The Apache Software Foundation. All rights +* reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* 3. The end-user documentation included with the redistribution, if +* any, must include the following acknowlegement: +* "This product includes software developed by the +* Apache Software Foundation (http://www.apache.org/)." +* Alternately, this acknowlegement may appear in the software itself, +* if and wherever such third-party acknowlegements normally appear. +* +* 4. The names "The Jakarta Project", "Struts", and "Apache Software +* Foundation" must not be used to endorse or promote products derived +* from this software without prior written permission. For written +* permission, please contact [EMAIL PROTECTED] +* +* 5. Products derived from this software may not be called "Apache" +* nor may "Apache" appear in their names without prior written +* permission of the Apache Group. +* +* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR +* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +* ==================================================================== +* +* This software consists of voluntary contributions made by many +* individuals on behalf of the Apache Software Foundation. For more +* information on the Apache Software Foundation, please see +* <http://www.apache.org/>. +* +*/ package org.apache.struts.webapp.upload; @@ -87,101 +87,106 @@ * page to display them * * @author Mike Schachter - * @version $Revision: 1.4 $ $Date: 2001/04/18 14:21:23 $ + * @version $Revision: 1.5 $ $Date: 2002/03/06 19:39:08 $ */ -public class UploadAction extends Action { - - public ActionForward perform(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) { - - if (form instanceof UploadForm) { - - UploadForm theForm = (UploadForm) form; - - //retrieve the text data - String text = theForm.getTheText(); - - //retrieve the file representation - FormFile file = theForm.getTheFile(); - - //retrieve the file name - String fileName= file.getFileName(); - - //retrieve the content type - String contentType = file.getContentType(); - - boolean writeFile = theForm.getWriteFile(); - - //retrieve the file size - String size = (file.getFileSize() + " bytes"); - - String data = null; - - try { - //retrieve the file data - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - InputStream stream = file.getInputStream(); - if (!writeFile) { - //only write files out that are less than 1MB - if (file.getFileSize() < (4*1024000)) { - - byte[] buffer = new byte[8192]; - int bytesRead = 0; - while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) { - baos.write(buffer, 0, bytesRead); - } - data = new String(baos.toByteArray()); - } - else { - data = new String("The file is greater than 4MB, " + - " and has not been written to stream." + - " File Size: " + file.getFileSize() + " bytes. This is a" + - " limitation of this particular web application, hard-coded" + - " in org.apache.struts.webapp.upload.UploadAction"); - } - } - else { - //write the file to the file specified - OutputStream bos = new FileOutputStream(theForm.getFilePath()); - int bytesRead = 0; - byte[] buffer = new byte[8192]; - while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) { - bos.write(buffer, 0, bytesRead); - } - bos.close(); - data = "The file has been written to \"" + theForm.getFilePath() + "\""; - } - //close the stream - stream.close(); - } - catch (FileNotFoundException fnfe) { - return null; +public class UploadAction extends Action +{ + public ActionForward perform(ActionMapping mapping, ActionForm form, + HttpServletRequest request, HttpServletResponse response) + { + + if (form instanceof UploadForm) { + + //this line is here for when the input page is upload-utf8.jsp, + //it sets the correct character encoding for the response + String encoding = request.getCharacterEncoding(); + if ((encoding != null) && (encoding.equalsIgnoreCase("utf-8"))) + { + response.setContentType("text/html; charset=utf-8"); + } + + UploadForm theForm = (UploadForm) form; + + //retrieve the text data + String text = theForm.getTheText(); + + //retrieve the file representation + FormFile file = theForm.getTheFile(); + + //retrieve the file name + String fileName= file.getFileName(); + + //retrieve the content type + String contentType = file.getContentType(); + + boolean writeFile = theForm.getWriteFile(); + + //retrieve the file size + String size = (file.getFileSize() + " bytes"); + + String data = null; + + try { + //retrieve the file data + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InputStream stream = file.getInputStream(); + if (!writeFile) { + //only write files out that are less than 1MB + if (file.getFileSize() < (4*1024000)) { + + byte[] buffer = new byte[8192]; + int bytesRead = 0; + while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) { + baos.write(buffer, 0, bytesRead); } - catch (IOException ioe) { - return null; - } - - //place the data into the request for retrieval from display.jsp - request.setAttribute("text", text); - request.setAttribute("fileName", fileName); - request.setAttribute("contentType", contentType); - request.setAttribute("size", size); - request.setAttribute("data", data); + data = new String(baos.toByteArray()); + } + else { + data = new String("The file is greater than 4MB, " + + " and has not been written to stream." + + " File Size: " + file.getFileSize() + " bytes. This is a" + + " limitation of this particular web application, hard-coded" + + " in org.apache.struts.webapp.upload.UploadAction"); + } + } + else { + //write the file to the file specified + OutputStream bos = new FileOutputStream(theForm.getFilePath()); + int bytesRead = 0; + byte[] buffer = new byte[8192]; + while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) { + bos.write(buffer, 0, bytesRead); + } + bos.close(); + data = "The file has been written to \"" + theForm.getFilePath() + "\""; + } + //close the stream + stream.close(); + } + catch (FileNotFoundException fnfe) { + return null; + } + catch (IOException ioe) { + return null; + } - //destroy the temporary file created - file.destroy(); + //place the data into the request for retrieval from display.jsp + request.setAttribute("text", text); + request.setAttribute("fileName", fileName); + request.setAttribute("contentType", contentType); + request.setAttribute("size", size); + request.setAttribute("data", data); - //return a forward to display.jsp - return mapping.findForward("display"); - } + //destroy the temporary file created + file.destroy(); - //this shouldn't happen in this example - return null; + //return a forward to display.jsp + return mapping.findForward("display"); } - + //this shouldn't happen in this example + return null; + } } 1.4 +167 -142 jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadForm.java Index: UploadForm.java =================================================================== RCS file: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadForm.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- UploadForm.java 14 Apr 2001 12:54:09 -0000 1.3 +++ UploadForm.java 6 Mar 2002 19:39:08 -0000 1.4 @@ -1,69 +1,72 @@ /* - * $Header: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadForm.java,v 1.3 2001/04/14 12:54:09 rleland Exp $ - * $Revision: 1.3 $ - * $Date: 2001/04/14 12:54:09 $ - * - * ==================================================================== - * - * The Apache Software License, Version 1.1 - * - * Copyright (c) 1999-2001 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowlegement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowlegement may appear in the software itself, - * if and wherever such third-party acknowlegements normally appear. - * - * 4. The names "The Jakarta Project", "Struts", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact [EMAIL PROTECTED] - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Group. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ +* $Header: /home/cvs/jakarta-struts/src/upload/org/apache/struts/webapp/upload/UploadForm.java,v 1.4 2002/03/06 19:39:08 mschachter Exp $ +* $Revision: 1.4 $ +* $Date: 2002/03/06 19:39:08 $ +* +* ==================================================================== +* +* The Apache Software License, Version 1.1 +* +* Copyright (c) 1999-2001 The Apache Software Foundation. All rights +* reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* 3. The end-user documentation included with the redistribution, if +* any, must include the following acknowlegement: +* "This product includes software developed by the +* Apache Software Foundation (http://www.apache.org/)." +* Alternately, this acknowlegement may appear in the software itself, +* if and wherever such third-party acknowlegements normally appear. +* +* 4. The names "The Jakarta Project", "Struts", and "Apache Software +* Foundation" must not be used to endorse or promote products derived +* from this software without prior written permission. For written +* permission, please contact [EMAIL PROTECTED] +* +* 5. Products derived from this software may not be called "Apache" +* nor may "Apache" appear in their names without prior written +* permission of the Apache Group. +* +* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR +* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +* ==================================================================== +* +* This software consists of voluntary contributions made by many +* individuals on behalf of the Apache Software Foundation. For more +* information on the Apache Software Foundation, please see +* <http://www.apache.org/>. +* +*/ package org.apache.struts.webapp.upload; import org.apache.struts.upload.FormFile; -import org.apache.struts.action.ActionForm; +import org.apache.struts.upload.MultipartRequestHandler; +import org.apache.struts.action.*; + +import javax.servlet.http.HttpServletRequest; /** @@ -74,90 +77,112 @@ * that struts uses is org.apache.struts.upload.DiskMultipartRequestHandler. * * @author Mike Schachter - * @version $Revision: 1.3 $ $Date: 2001/04/14 12:54:09 $ + * @version $Revision: 1.4 $ $Date: 2002/03/06 19:39:08 $ */ -public class UploadForm extends ActionForm { - - /** - * The value of the text the user has sent as form data - */ - protected String theText; - - /** - * Whether or not to write to a file - */ - protected boolean writeFile; - - /** - * The file that the user has uploaded - */ - protected FormFile theFile; - - /** - * The file path to write to - */ - protected String filePath; - - - - /** - * Retrieve the value of the text the user has sent as form data - */ - public String getTheText() { - return theText; - } - - /** - * Set the value of the form data text - */ - public void setTheText(String theText) { - this.theText = theText; - } - - /** - * Retrieve a representation of the file the user has uploaded - */ - public FormFile getTheFile() { - return theFile; - } - - /** - * Set a representation of the file the user has uploaded - */ - public void setTheFile(FormFile theFile) { - this.theFile = theFile; - } - - /** - * Set whether or not to write to a file - */ - public void setWriteFile(boolean writeFile) { - this.writeFile = writeFile; - } - - /** - * Get whether or not to write to a file - */ - public boolean getWriteFile() { - return writeFile; - } - - /** - * Set the path to write a file to - */ - public void setFilePath(String filePath) { - this.filePath = filePath; - } - - /** - * Get the path to write a file to - */ - public String getFilePath() { - return filePath; +public class UploadForm extends ActionForm +{ + public static final String ERROR_PROPERTY_MAX_LENGTH_EXCEEDED = "org.apache.struts.webapp.upload.MaxLengthExceeded"; + + + /** + * The value of the text the user has sent as form data + */ + protected String theText; + + /** + * Whether or not to write to a file + */ + protected boolean writeFile; + + /** + * The file that the user has uploaded + */ + protected FormFile theFile; + + /** + * The file path to write to + */ + protected String filePath; + + + + /** + * Retrieve the value of the text the user has sent as form data + */ + public String getTheText() { + return theText; + } + + /** + * Set the value of the form data text + */ + public void setTheText(String theText) { + this.theText = theText; + } + + /** + * Retrieve a representation of the file the user has uploaded + */ + public FormFile getTheFile() { + return theFile; + } + + /** + * Set a representation of the file the user has uploaded + */ + public void setTheFile(FormFile theFile) { + this.theFile = theFile; + } + + /** + * Set whether or not to write to a file + */ + public void setWriteFile(boolean writeFile) { + this.writeFile = writeFile; + } + + /** + * Get whether or not to write to a file + */ + public boolean getWriteFile() { + return writeFile; + } + + /** + * Set the path to write a file to + */ + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + /** + * Get the path to write a file to + */ + public String getFilePath() { + return filePath; + } + + public void reset() { + writeFile = false; + } + + /** + * Check to make sure the client hasn't exceeded the maximum allowed upload size inside of this + * validate method. + */ + public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) + { + ActionErrors errors = null; + //has the maximum length been exceeded? + Boolean maxLengthExceeded = (Boolean) + request.getAttribute(MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED); + if ((maxLengthExceeded != null) && (maxLengthExceeded.booleanValue())) + { + errors = new ActionErrors(); + errors.add(ERROR_PROPERTY_MAX_LENGTH_EXCEEDED, new ActionError("maxLengthExceeded")); } + return errors; - public void reset() { - writeFile = false; - } + } } 1.4 +37 -6 jakarta-struts/web/upload/upload.jsp Index: upload.jsp =================================================================== RCS file: /home/cvs/jakarta-struts/web/upload/upload.jsp,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- upload.jsp 11 Apr 2001 22:56:28 -0000 1.3 +++ upload.jsp 6 Mar 2002 19:39:09 -0000 1.4 @@ -1,18 +1,47 @@ +<%@ page import="org.apache.struts.action.*, + java.util.Iterator, + org.apache.struts.webapp.upload.UploadForm"%> <%@ page language="java" %> - <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> +<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> +<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> + +<html> +<head> +<title>File Upload Example</title> +</head> + +<body> +<!-- Find out if the maximum length has been exceeded. --> +<logic:present name="<%= Action.ERROR_KEY %>" scope="request"> + <% + ActionErrors errors = (ActionErrors) request.getAttribute(Action.ERROR_KEY); + //note that this error is created in the validate() method of UploadForm + Iterator iterator = errors.get(UploadForm.ERROR_PROPERTY_MAX_LENGTH_EXCEEDED); + //there's only one possible error in this + ActionError error = (ActionError) iterator.next(); + pageContext.setAttribute("maxlength.error", error, PageContext.REQUEST_SCOPE); + %> +</logic:present> +<!-- If there was an error, print it out --> +<logic:present name="maxlength.error" scope="request"> + <font color="red"><bean:message name="maxlength.error" property="key" /></font> +</logic:present> +<logic:notPresent name="maxlength.error" scope="request"> + Note that the maximum allowed size of an uploaded file for this application is two megabytes. + See the /WEB-INF/struts-config.xml file for this application to change it. +</logic:notPresent> +<br /><br /> <!-- The most important part is to declare your form's enctype to be "multipart/form-data", and to have a form:file element that maps to your ActionForm's FormFile property --> - - <html:form action="upload.do" enctype="multipart/form-data"> Please enter some text, just to demonstrate the handling of text elements as opposed to file elements:<br /> <html:text property="theText" /><br /><br /> - + Please select the file that you would like to upload:<br /> <html:file property="theFile" /><br /><br /> @@ -21,8 +50,10 @@ If you checked the box to write to a file, please specify the file path here:<br /> <html:text property="filePath" /><br /><br /> - + <html:submit /> -</html:form> \ No newline at end of file +</html:form> +</body> +</html> \ No newline at end of file 1.4 +5 -10 jakarta-struts/web/upload/WEB-INF/struts-config.xml Index: struts-config.xml =================================================================== RCS file: /home/cvs/jakarta-struts/web/upload/WEB-INF/struts-config.xml,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- struts-config.xml 25 Jun 2001 00:02:34 -0000 1.3 +++ struts-config.xml 6 Mar 2002 19:39:09 -0000 1.4 @@ -7,28 +7,23 @@ <struts-config> - - + <!-- This is where you set the maximum file size for your file uploads. -1 is an unlimited size. If you + don't specify this here, it defaults to -1. --> + <controller maxFileSize="2M" /> <form-beans> - - <form-bean name="uploadForm" type="org.apache.struts.webapp.upload.UploadForm"/> - </form-beans> - <action-mappings> - <!-- Upload Action --> <action path="/upload" type="org.apache.struts.webapp.upload.UploadAction" - name="uploadForm"> + name="uploadForm" + input="/upload.jsp"> <forward name="display" path="/display.jsp" /> </action> - </action-mappings> - </struts-config> 1.4 +6 -2 jakarta-struts/web/upload/WEB-INF/web.xml Index: web.xml =================================================================== RCS file: /home/cvs/jakarta-struts/web/upload/WEB-INF/web.xml,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- web.xml 25 Jun 2001 00:02:34 -0000 1.3 +++ web.xml 6 Mar 2002 19:39:09 -0000 1.4 @@ -23,6 +23,10 @@ <param-name>detail</param-name> <param-value>2</param-value> </init-param> + <init-param> + <param-name>application</param-name> + <param-value>org.apache.struts.webapp.upload.UploadResources</param-value> + </init-param> <load-on-startup>1</load-on-startup> </servlet> @@ -39,10 +43,10 @@ <welcome-file>upload.jsp</welcome-file> </welcome-file-list> - + <taglib> <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/struts-html.tld</taglib-location> </taglib> - + </web-app>
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>