📄 multipartboundaryinputstream.java
字号:
/*
* $Header: /sfroot/cvs/esimple/src/core/org/apache/struts/upload/MultipartBoundaryInputStream.java,v 1.1.1.1 2004/09/08 06:38:41 lava Exp $
* $Revision: 1.1.1.1 $
* $Date: 2004/09/08 06:38:41 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2002 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 apache@apache.org.
*
* 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.upload;
import java.io.InputStream;
import java.io.IOException;
import java.io.File;
/**
* This class encapsulates parsing functionality for RFC1867, multipart/form-data. See MultipartBoundaryInputStreamTest
* and MultipartIterator for usage examples.
*
* @author Mike Schachter
*/
public class MultipartBoundaryInputStream extends InputStream
{
private static final byte NEWLINE_BYTE = ((byte) '\n');
private static final byte CARRIAGE_RETURN = ((byte) '\r');
private static final byte[] CRLF = new byte[] {CARRIAGE_RETURN, NEWLINE_BYTE};
private static final String DOUBLE_DASH_STRING = "--";
private static final int DEFAULT_LINE_SIZE = 4096;
private static final String TOKEN_EQUALS = "=";
private static final char TOKEN_QUOTE = '\"';
private static final char TOKEN_COLON = ':';
private static final char TOKEN_SEMI_COLON = ';';
private static final char TOKEN_SPACE = ' ';
private static final String DEFAULT_CONTENT_DISPOSITION = "form-data";
private static final String PARAMETER_NAME = "name";
private static final String PARAMETER_FILENAME = "filename";
private static final String PARAMETER_CHARSET = "charset";
private static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain";
private static final String CONTENT_TYPE_APPLICATION_OCTET_STREAM = "application/octet-stream";
private static final String MESSAGE_INVALID_START = "Multipart data doesn't start with boundary";
/**
* The InputStream to read from.
*/
protected InputStream inputStream;
/**
* The boundary.
*/
protected String boundary;
/**
* Whether or not the boundary has been encountered.
*/
protected boolean boundaryEncountered;
/**
* Whether or not the final boundary has been encountered.
*/
protected boolean finalBoundaryEncountered;
/**
* Whether or not the end of the stream has been read.
*/
protected boolean endOfStream;
/**
* The Content-Disposition for the current form element being read.
*/
protected String elementContentDisposition;
/**
* The name of the current form element being read.
*/
protected String elementName;
/**
* The Content-Type of the current form element being read.
*/
protected String elementContentType;
/**
* The filename of the current form element being read, <code>null</code> if the current form element is
* text data.
*/
protected String elementFileName;
/**
* The character encoding of the element, specified in the element's Content-Type header.
*/
protected String elementCharset;
/**
* The maximum length in bytes to read from the stream at a time, or -1 for unlimited length.
*/
protected long maxLength;
/**
* Whether or not the maximum length has been met.
*/
protected boolean maxLengthMet;
/**
* The total number of bytes read so far.
*/
protected long bytesRead;
private byte[] boundaryBytes;
private byte[] finalBoundaryBytes;
private byte[] line;
private int lineSize;
private int lineLength;
private boolean lineHasNewline;
private boolean lineHasCarriage;
private int lineIndex;
public MultipartBoundaryInputStream()
{
this.lineSize = DEFAULT_LINE_SIZE;
this.maxLength = -1;
resetStream();
}
/**
* Sets the boundary that terminates the data for the stream, after adding the prefix "--"
*/
public void setBoundary(String boundary)
{
this.boundary = DOUBLE_DASH_STRING + boundary;
this.boundaryBytes = this.boundary.getBytes();
this.finalBoundaryBytes = (this.boundary + DOUBLE_DASH_STRING).getBytes();
}
/**
* Resets this stream for use with the next element, to be used after a boundary is encountered.
*/
public void resetForNextBoundary() throws IOException
{
if (!this.finalBoundaryEncountered)
{
this.boundaryEncountered = false;
resetCrlf();
fillLine();
readElementHeaders();
}
}
/**
* Sets the input stream used to read multipart data. For efficiency purposes, make sure that the stream
* you set on this class is buffered. The way this class reads lines is that it continually calls the read()
* method until it reaches a newline character. That would be terrible if you were to set a socket's input stream
* here, but not as bad on a buffered stream.
*/
public void setInputStream(InputStream stream) throws IOException
{
this.inputStream = stream;
resetStream();
readFirstElement();
}
/**
* Reads from the stream. Returns -1 if it's the end of the stream or if a boundary is encountered.
*/
public int read() throws IOException
{
if (!this.maxLengthMet)
{
if (!this.boundaryEncountered)
{
return readFromLine();
}
}
return -1;
}
public int read(byte[] buffer) throws IOException
{
return read(buffer, 0, buffer.length);
}
public int read(byte[] buffer, int offset, int length) throws IOException
{
if (length > 0)
{
int read = read();
if ((read == -1) && (this.endOfStream || this.boundaryEncountered))
{
return -1;
}
int bytesRead = 1;
buffer[offset++] = (byte) read;
while ((bytesRead < length) && (((read = read())!= -1) || ((read == -1) &&
(!this.boundaryEncountered))) && !this.maxLengthMet)
{
buffer[offset++] = (byte) read;
bytesRead++;
}
return bytesRead;
}
return -1;
}
/**
* Marks the underlying stream.
*/
public synchronized void mark(int i)
{
this.inputStream.mark(i);
}
/**
* Resets the underlying input stream.
*/
public synchronized void reset() throws IOException
{
this.inputStream.reset();
}
/**
* Set the maximum length in bytes to read, or -1 for an unlimited length.
*/
public void setMaxLength(long maxLength)
{
this.maxLength = maxLength;
}
public long getMaxLength()
{
return maxLength;
}
/**
* Whether or not the maximum length has been met.
*/
public boolean isMaxLengthMet()
{
return maxLengthMet;
}
/**
* Gets the value for the "Content-Dispositio" header for the current multipart element.
* Usually "form-data".
*/
public String getElementContentDisposition()
{
return this.elementContentDisposition;
}
/**
* Gets the name of the current element. The name corresponds to the value of
* the "name" attribute of the form element.
*/
public String getElementName()
{
return this.elementName;
}
/**
* Gets the character encoding of the current element. The character encoding would have been specified
* in the Content-Type header for this element, if it wasn't this is null.
*/
public String getElementCharset()
{
return this.elementCharset;
}
/**
* Gets the "Content-Type" of the current element. If this is a text element,
* the content type will probably be "text/plain", otherwise it will be the
* content type of the file element.
*/
public String getElementContentType()
{
return this.elementContentType;
}
/**
* Gets the filename of the current element, which will be null if the current element
* isn't a file.
*/
public String getElementFileName()
{
return this.elementFileName;
}
/**
* Gets whether or not the current form element being read is a file.
*/
public boolean isElementFile()
{
return (this.elementFileName != null);
}
/**
* Returns whether or not the boundary has been encountered while reading data.
*/
public boolean isBoundaryEncountered()
{
return this.boundaryEncountered;
}
/**
* Returns whether or not the final boundary has been encountered.
*/
public boolean isFinalBoundaryEncountered()
{
return this.finalBoundaryEncountered;
}
/**
* Whether or not an EOF has been read on the stream.
*/
public boolean isEndOfStream()
{
return this.endOfStream;
}
public void setLineSize(int size)
{
this.lineSize = size;
}
public long getBytesRead()
{
return this.bytesRead;
}
private final void readFirstElement() throws IOException
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -