看看这个对象的getParameter的方法
// Copyright (C) 1998 by Jason Hunter <[email protected]>. All rights reserved.
// Use of this class is limited. Please see the LICENSE for more information.package com.oreilly.servlet;import java.io.*;
import java.util.*;
import javax.servlet.*;
import java.lang.*;
import javax.servlet.http.*;/**
* A utility class to handle <tt>multipart/form-data</tt> requests,
* the kind of requests that support file uploads. This class can
* receive arbitrarily large files (up to an artificial limit you can set),
* and fairly efficiently too.
* It cannot handle nested data (multipart content within multipart content)
* or internationalized content (such as non Latin-1 filenames).
* <p>
* It's used like this:
* <blockquote><pre>
* MultipartRequest multi = new MultipartRequest(req, ".");
*
* out.println("Params:");
* Enumeration params = multi.getParameterNames();
* while (params.hasMoreElements()) {
* String name = (String)params.nextElement();
* String value = multi.getParameter(name);
* out.println(name + " = " + value);
* }
* out.println();
*
* out.println("Files:");
* Enumeration files = multi.getFileNames();
* while (files.hasMoreElements()) {
* String name = (String)files.nextElement();
* String filename = multi.getFilesystemName(name);
* String type = multi.getContentType(name);
* File f = multi.getFile(name);
* out.println("name: " + name);
* out.println("filename: " + filename);
* out.println("type: " + type);
* if (f != null) {
* out.println("f.toString(): " + f.toString());
* out.println("f.getName(): " + f.getName());
* out.println("f.exists(): " + f.exists());
* out.println("f.length(): " + f.length());
* out.println();
* }
* }
* </pre></blockquote>
*
* A client can upload files using an HTML form with the following structure.
* Note that not all browsers support file uploads.
* <blockquote><pre>
* <FORM ACTION="/servlet/Handler" METHOD=POST
* ENCTYPE="multipart/form-data">
* What is your name? <INPUT TYPE=TEXT NAME=submitter> <BR>
* Which file to upload? <INPUT TYPE=FILE NAME=file> <BR>
* <INPUT TYPE=SUBMIT>
* </FORM>
* </pre></blockquote>
* <p>
* The full file upload specification is contained in experimental RFC 1867,
* available at <a href="http://www.ietf.org/rfc/rfc1867.txt">
* http://www.ietf.org/rfc/rfc1867.txt</a>.
*
* @author <b>Jason Hunter</b>, Copyright © 1998-1999
* @version 1.6, 00/03/19, better WebSphere 2.x/3.x content type workaround
* @version 1.5, 00/02/04, added auto MacBinary decoding for IE on Mac
* @version 1.4, 00/01/05, added getParameterValues(),
* WebSphere 2.x getContentType() workaround,
* stopped writing empty "unknown" file
* @version 1.3, 99/12/28, IE4 on Win98 lastIndexOf("boundary=") workaround
* @version 1.2, 99/12/20, IE4 on Mac readNextPart() workaround
* @version 1.1, 99/01/15, JSDK readLine() bug workaround
* @version 1.0, 98/09/18
*/
public class MultipartRequest { private static final int DEFAULT_MAX_POST_SIZE = 1024 * 1024 * 10; // 1 Meg
private static final String NO_FILE = "unknown"; private HttpServletRequest req;
private File dir=null;
private int maxSize;
private String tempFilename; //add by lzl
private boolean defaultFileFlag=false; //add by lzl
private int fileNo=1; //add by lzl
private Hashtable fileFullPaths; // add by wguzgg
private Hashtable fileLengths; //add by wguzgg
private Hashtable fileLocalPaths; // add by wguzgg
private Hashtable parameters = new Hashtable(); // name - Vector of values
private Hashtable files = new Hashtable(); // name - UploadedFile
//add by wguzgg
public long getFileLengths(String fn) {
Long fl=(Long)fileLengths.get(fn);
return fl.longValue();
} //add by wguzgg
public String getFileFullPath(String fn) {
String fp=(String)fileFullPaths.get(fn);
return fp;
} //add by wguzgg
public String getFileLocalPath(String fn) {
String flp=(String)fileLocalPaths.get(fn);
return flp;
} //add by lzl
public MultipartRequest(HttpServletRequest request,String saveDirectory,boolean fileFlag) throws IOException {
this(request, saveDirectory, DEFAULT_MAX_POST_SIZE,fileFlag);
} /**
* Constructs a new MultipartRequest to handle the specified request,
* saving any uploaded files to the given directory, and limiting the
* upload size to 1 Megabyte. If the content is too large, an
* IOException is thrown. This constructor actually parses the
* <tt>multipart/form-data</tt> and throws an IOException if there's any
* problem reading or parsing the request.
*
* @param request the servlet request
* @param saveDirectory the directory in which to save any uploaded files
* @exception IOException if the uploaded content is larger than 1 Megabyte
* or there's a problem reading or parsing the request
*/
public MultipartRequest(HttpServletRequest request,
String saveDirectory) throws IOException {
this(request, saveDirectory, DEFAULT_MAX_POST_SIZE);
} /**
* Constructs a new MultipartRequest to handle the specified request,
* saving any uploaded files to the given directory, and limiting the
* upload size to the specified length. If the content is too large, an
* IOException is thrown. This constructor actually parses the
* <tt>multipart/form-data</tt> and throws an IOException if there's any
* problem reading or parsing the request.
*
* @param request the servlet request
* @param saveDirectory the directory in which to save any uploaded files
* @param maxPostSize the maximum size of the POST content
* @exception IOException if the uploaded content is larger than
* <tt>maxPostSize</tt> or there's a problem reading or parsing the request
*/
public MultipartRequest(HttpServletRequest request,
String saveDirectory,
int maxPostSize) throws IOException {
// Sanity check values
if (request == null)
throw new IllegalArgumentException("request cannot be null");
if (saveDirectory == null)
throw new IllegalArgumentException("saveDirectory cannot be null");
if (maxPostSize <= 0) {
throw new IllegalArgumentException("maxPostSize must be positive");
} // Save the request, dir, and max size
req = request;
dir = new File(saveDirectory);
maxSize = maxPostSize;
tempFilename=saveDirectory.substring(saveDirectory.lastIndexOf("/"));
// Check saveDirectory is truly a directory
if (!dir.isDirectory())
throw new IllegalArgumentException("Not a directory: " + saveDirectory); // Check saveDirectory is writable
if (!dir.canWrite())
throw new IllegalArgumentException("Not writable: " + saveDirectory); // Now parse the request saving data to "parameters" and "files";
// write the file contents to the saveDirectory
readRequest();
} //add by lzl
public MultipartRequest(HttpServletRequest request,
String saveDirectory,
int maxPostSize,boolean fileFlag) throws IOException {
// Sanity check values
if (request == null)
throw new IllegalArgumentException("request cannot be null");
if (saveDirectory == null)
throw new IllegalArgumentException("saveDirectory cannot be null");
if (maxPostSize <= 0) {
throw new IllegalArgumentException("maxPostSize must be positive");
} // Save the request, dir, and max size
req = request;
dir = new File(saveDirectory);
maxSize = maxPostSize;
tempFilename=saveDirectory.substring(saveDirectory.lastIndexOf("/"));
defaultFileFlag=fileFlag;
// Check saveDirectory is truly a directory
if (!dir.isDirectory())
throw new IllegalArgumentException("Not a directory: " + saveDirectory); // Check saveDirectory is writable
if (!dir.canWrite())
throw new IllegalArgumentException("Not writable: " + saveDirectory); // Now parse the request saving data to "parameters" and "files";
// write the file contents to the saveDirectory
readRequest();
}
/**
* Constructor with an old signature, kept for backward compatibility.
* Without this constructor, a servlet compiled against a previous version
* of this class (pre 1.4) would have to be recompiled to link with this
* version. This constructor supports the linking via the old signature.
* Callers must simply be careful to pass in an HttpServletRequest.
*
*/
public MultipartRequest(ServletRequest request,
String saveDirectory) throws IOException {
this((HttpServletRequest)request, saveDirectory);
} /**
* Constructor with an old signature, kept for backward compatibility.
* Without this constructor, a servlet compiled against a previous version
* of this class (pre 1.4) would have to be recompiled to link with this
* version. This constructor supports the linking via the old signature.
* Callers must simply be careful to pass in an HttpServletRequest.
*
*/
public MultipartRequest(ServletRequest request,
String saveDirectory,
int maxPostSize) throws IOException {
this((HttpServletRequest)request, saveDirectory, maxPostSize);
} /**
* Returns the names of all the parameters as an Enumeration of
* Strings. It returns an empty Enumeration if there are no parameters.
*
* @return the names of all the parameters as an Enumeration of Strings
*/
public Enumeration getParameterNames() {
return parameters.keys();
} /**
* Returns the names of all the uploaded files as an Enumeration of
* Strings. It returns an empty Enumeration if there are no uploaded
* files. Each file name is the name specified by the form, not by
* the user.
*
* @return the names of all the uploaded files as an Enumeration of Strings
*/
public Enumeration getFileNames() {
return files.keys();
} /**
* Returns the value of the named parameter as a String, or null if
* the parameter was not sent or was sent without a value. The value
* is guaranteed to be in its normal, decoded form. If the parameter
* has multiple values, only the last one is returned (for backward
* compatibility). For parameters with multiple values, it's possible
* the last "value" may be null.
*
* @param name the parameter name
* @return the parameter value
*/
public String getParameter(String name) {
try {
Vector values = (Vector)parameters.get(name);
if (values == null || values.size() == 0) {
return null;
}
String value = (String)values.elementAt(values.size() - 1);
return value;
}
catch (Exception e) {
return null;
}
} /**
* Returns the values of the named parameter as a String array, or null if
* the parameter was not sent. The array has one entry for each parameter
* field sent. If any field was sent without a value that entry is stored
* in the array as a null. The values are guaranteed to be in their
* normal, decoded form. A single value is returned as a one-element array.
*
* @param name the parameter name
* @return the parameter values
*/
public String[] getParameterValues(String name) {
try {
Vector values = (Vector)parameters.get(name);
if (values == null || values.size() == 0) {
return null;
}
String[] valuesArray = new String[values.size()];
values.copyInto(valuesArray);
return valuesArray;
}
catch (Exception e) {
return null;
}
} /**
* Returns the filesystem name of the specified file, or null if the
* file was not included in the upload. A filesystem name is the name
* specified by the user. It is also the name under which the file is
* actually saved.
*
* @param name the file name
* @return the filesystem name of the file
*/
public String getFilesystemName(String name) {
try {
UploadedFile file = (UploadedFile)files.get(name);
return file.getFilesystemName(); // may be null
}
catch (Exception e) {
return null;
}
} /**
* Returns the content type of the specified file (as supplied by the
* client browser), or null if the file was not included in the upload.
*
* @param name the file name
* @return the content type of the file
*/
public String getContentType(String name) {
try {
UploadedFile file = (UploadedFile)files.get(name);
return file.getContentType(); // may be null
}
catch (Exception e) {
return null;
}
} /**
* Returns a File object for the specified file saved on the server's
* filesystem, or null if the file was not included in the upload.
*
* @param name the file name
* @return a File object for the named file
*/
public File getFile(String name) {
try {
UploadedFile file = (UploadedFile)files.get(name);
return file.getFile(); // may be null
}
catch (Exception e) {
return null;
}
} /**
* The workhorse method that actually parses the request. A subclass
* can override this method for a better optimized or differently
* behaved implementation.
*
* @exception IOException if the uploaded content is larger than
* <tt>maxSize</tt> or there's a problem parsing the request
*/
protected void readRequest() throws IOException {
// Check the content length to prevent denial of service attacks
int length = req.getContentLength();
if (length > maxSize) {
throw new IOException("Posted content length of " + length +
" exceeds limit of " + maxSize);
} // Check the content type to make sure it's "multipart/form-data"
// Access header two ways to work around WebSphere oddities
String type = null;
String type1 = req.getHeader("Content-Type");
String type2 = req.getContentType();
// If one value is null, choose the other value
if (type1 == null && type2 != null) {
type = type2;
}
else if (type2 == null && type1 != null) {
type = type1;
}
// If neither value is null, choose the longer value
else if (type1 != null && type2 != null) {
type = (type1.length() > type2.length() ? type1 : type2);
} if (type == null ||
!type.toLowerCase().startsWith("multipart/form-data")) {
throw new IOException("Posted content type isn't multipart/form-data");
}
// Get the boundary string; it's included in the content type.
// Should look something like "------------------------12012133613061"
String boundary = extractBoundary(type);
if (boundary == null) {
throw new IOException("Separation boundary was not specified");
} // Construct the special input stream we'll read from
MultipartInputStreamHandler in =
new MultipartInputStreamHandler(req.getInputStream(), length); // Read the first line, should be the first boundary
String line = in.readLine();
if (line == null) {
throw new IOException("Corrupt form data: premature ending");
} // Verify that the line is the boundary
if (!line.startsWith(boundary)) {
throw new IOException("Corrupt form data: no leading boundary");
} // Now that we're just beyond the first boundary, loop over each part
boolean done = false;
while (!done) {
done = readNextPart(in, boundary);
}
} /**
* A utility method that reads an individual part. Dispatches to
* readParameter() and readAndSaveFile() to do the actual work. A
* subclass can override this method for a better optimized or
* differently behaved implementation.
*
* @param in the stream from which to read the part
* @param boundary the boundary separating parts
* @return a flag indicating whether this is the last part
* @exception IOException if there's a problem reading or parsing the
* request
*
* @see readParameter
* @see readAndSaveFile
*/
protected boolean readNextPart(MultipartInputStreamHandler in,
String boundary) throws IOException {
// Read the first line, should look like this:
// content-disposition: form-data; name="field1"; filename="file1.txt"
String line = in.readLine();
if (line == null) {
// No parts left, we're done
return true;
}
else if (line.length() == 0) {
// IE4 on Mac sends an empty line at the end; treat that as the end.
// Thanks to Daniel Lemire and Henri Tourigny for this fix.
return true;
} // Parse the content-disposition line
String[] dispInfo = extractDispositionInfo(line);
String disposition = dispInfo[0];
String name = dispInfo[1];
String filename = dispInfo[2]; // Now onto the next line. This will either be empty
// or contain a Content-Type and then an empty line.
line = in.readLine();
if (line == null) {
// No parts left, we're done
return true;
} // Get the content type, or null if none specified
String contentType = extractContentType(line);
if (contentType != null) {
// Eat the empty line
line = in.readLine();
if (line == null || line.length() > 0) { // line should be empty
throw new
IOException("Malformed line after content type: " + line);
}
}
else {
// Assume a default content type
contentType = "application/octet-stream";
} // Now, finally, we read the content (end after reading the boundary)
if (filename == null) {
// This is a parameter, add it to the vector of values
String value = readParameter(in, boundary);
if (value.equals("")) {
value = null; // treat empty strings like nulls
}
Vector existingValues = (Vector)parameters.get(name);
if (existingValues == null) {
existingValues = new Vector();
parameters.put(name, existingValues);
}
existingValues.addElement(value);
}
else {
// This is a file
readAndSaveFile(in, boundary, filename, contentType,name);
if (fileLocalPaths==null) //add by wguzgg
fileLocalPaths=new Hashtable(); //add by wguzgg
fileLocalPaths.put(new String(filename),dir.getAbsolutePath()); //add by wguzgg
if (fileLengths==null) //add by wguzgg
fileLengths=new Hashtable(); //add by wguzgg
File f = new File(dir + File.separator + filename); //add by wguzgg
Long fileLength=new Long(f.length()); //add by wguzgg
fileLengths.put(new String(filename),fileLength); //add by wguzgg
if (filename.equals(NO_FILE)) {
files.put(name, new UploadedFile(null, null, null));
}
else {
if(fileNo==1 && !defaultFileFlag){
String fileType=filename.substring(filename.indexOf("."));
filename=""+tempFilename+fileType.trim();
}
files.put(name,new UploadedFile(dir.toString(), filename, contentType));
fileNo++;
}
}
return false; // there's more to read
} /**
* A utility method that reads a single part of the multipart request
* that represents a parameter. A subclass can override this method
* for a better optimized or differently behaved implementation.
*
* @param in the stream from which to read the parameter information
* @param boundary the boundary signifying the end of this part
* @return the parameter value
* @exception IOException if there's a problem reading or parsing the
* request
*/
protected String readParameter(MultipartInputStreamHandler in,
String boundary) throws IOException {
StringBuffer sbuf = new StringBuffer();
String line; while ((line = in.readLine()) != null) {
if (line.startsWith(boundary)) break;
sbuf.append(line + "\r\n"); // add the \r\n in case there are many lines
} if (sbuf.length() == 0) {
return null; // nothing read
} sbuf.setLength(sbuf.length() - 2); // cut off the last line's \r\n
return sbuf.toString(); // no URL decoding needed
} /**
* A utility method that reads a single part of the multipart request
* that represents a file, and saves the file to the given directory.
* A subclass can override this method for a better optimized or
* differently behaved implementation.
*
* @param in the stream from which to read the file
* @param boundary the boundary signifying the end of this part
* @param dir the directory in which to save the uploaded file
* @param filename the name under which to save the uploaded file
* @exception IOException if there's a problem reading or parsing the
* request
*/
protected void readAndSaveFile(MultipartInputStreamHandler in,
String boundary,
String filename,
String contentType,
String name) throws IOException {
OutputStream out = null;
// A filename of NO_FILE means no file was sent, so just read to the
// next boundary and ignore the empty contents
if (filename.equals(NO_FILE)) {
out = new ByteArrayOutputStream(); // write to nowhere
}
// A MacBinary file goes through a decoder
else if (contentType.equals("application/x-macbinary")){
File f = new File(dir + File.separator + filename);
out = new MacBinaryDecoderOutputStream(
new BufferedOutputStream(
new FileOutputStream(f), 8 * 1024));
}
// A real file's contents are written to disk
else {
if(fileNo==1 && !defaultFileFlag){
String fileType=filename.substring(filename.indexOf("."));
filename=""+tempFilename+fileType.trim();
}
File f = new File(dir + File.separator + filename);
out = new BufferedOutputStream(new FileOutputStream(f), 8 * 1024);
} byte[] bbuf = new byte[100 * 1024]; // 100K
int result;
String line; // ServletInputStream.readLine() has the annoying habit of
// adding a \r\n to the end of the last line.
// Since we want a byte-for-byte transfer, we have to cut those chars.
boolean rnflag = false;
while ((result = in.readLine(bbuf, 0, bbuf.length)) != -1) {
// Check for boundary
if (result > 2 && bbuf[0] == '-' && bbuf[1] == '-') { // quick pre-check
line = new String(bbuf, 0, result); //has edited by lzl
if (line.startsWith(boundary)) break;
}
// Are we supposed to write \r\n for the last iteration?
if (rnflag) {
out.write('\r'); out.write('\n');
rnflag = false;
}
// Write the buffer, postpone any ending \r\n
if (result >= 2 &&
bbuf[result - 2] == '\r' &&
bbuf[result - 1] == '\n') {
out.write(bbuf, 0, result - 2); // skip the last 2 chars
rnflag = true; // make a note to write them on the next iteration
}
else {
out.write(bbuf, 0, result);
}
}
out.flush();
out.close();
} // Extracts and returns the boundary token from a line.
//
private String extractBoundary(String line) {
// Use lastIndexOf() because IE 4.01 on Win98 has been known to send the
// "boundary=" string multiple times. Thanks to David Wall for this fix.
int index = line.lastIndexOf("boundary=");
if (index == -1) {
return null;
}
String boundary = line.substring(index + 9); // 9 for "boundary=" // The real boundary is always preceeded by an extra "--"
boundary = "--" + boundary; return boundary;
} // Extracts and returns disposition info from a line, as a String array
// with elements: disposition, name, filename. Throws an IOException
// if the line is malformatted.
//
private String[] extractDispositionInfo(String line) throws IOException {
// Return the line's data as an array: disposition, name, filename
String[] retval = new String[3]; // Convert the line to a lowercase string without the ending \r\n
// Keep the original line for error messages and for variable names.
String origline = line;
line = origline.toLowerCase(); // Get the content disposition, should be "form-data"
int start = line.indexOf("content-disposition: ");
int end = line.indexOf(";");
if (start == -1 || end == -1) {
throw new IOException("Content disposition corrupt: " + origline);
}
String disposition = line.substring(start + 21, end);
if (!disposition.equals("form-data")) {
throw new IOException("Invalid content disposition: " + disposition);
} // Get the field name
start = line.indexOf("name=\"", end); // start at last semicolon
end = line.indexOf("\"", start + 7); // skip name=\"
if (start == -1 || end == -1) {
throw new IOException("Content disposition corrupt: " + origline);
}
String name = origline.substring(start + 6, end); // Get the filename, if given
String filename = null;
start = line.indexOf("filename=\"", end + 2); // start after name
end = line.indexOf("\"", start + 10); // skip filename=\"
if (start != -1 && end != -1) { // note the !=
filename = origline.substring(start + 10, end);
String temp=new String(filename); //add by wguzgg
// The filename may contain a full path. Cut to just the filename.
int slash =
Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
if (slash > -1) {
filename = filename.substring(slash + 1); // past last slash
}
if (filename.equals("")) filename = NO_FILE; // sanity check
if (fileFullPaths==null) //add by wguzgg
fileFullPaths=new Hashtable(); //add by wguzgg
fileFullPaths.put(new String(filename),temp); //add by wguzgg
} // Return a String array: disposition, name, filename
retval[0] = disposition;
retval[1] = name;
retval[2] = filename;
return retval;
} // Extracts and returns the content type from a line, or null if the
// line was empty. Throws an IOException if the line is malformatted.
//
private String extractContentType(String line) throws IOException {
String contentType = null; // Convert the line to a lowercase string
String origline = line;
line = origline.toLowerCase(); // Get the content type, if any
if (line.startsWith("content-type")) {
int start = line.indexOf(" ");
if (start == -1) {
throw new IOException("Content type corrupt: " + origline);
}
contentType = line.substring(start + 1);
}
else if (line.length() != 0) { // no content type, so should be empty
throw new IOException("Malformed line after disposition: " + origline);
} return contentType;
}
}
// A class to hold information about an uploaded file.
//
class UploadedFile { private String dir;
private String filename;
private String type; UploadedFile(String dir, String filename, String type) {
this.dir = dir;
this.filename = filename;
this.type = type;
} public String getContentType() {
return type;
} public String getFilesystemName() {
return filename;
} public File getFile() {
if (dir == null || filename == null) {
return null;
}
else {
return new File(dir + File.separator + filename);
}
}
}
// A class to aid in reading multipart/form-data from a ServletInputStream.
// It keeps track of how many bytes have been read and detects when the
// Content-Length limit has been reached. This is necessary since some
// servlet engines are slow to notice the end of stream.
//
// Mac users: The Mac doesn't like class names which exceed 32 characters
// (including the ".class") so while this class is usable from a JAR
// anywhere, it won't compile on a Mac.
//
class MultipartInputStreamHandler { ServletInputStream in;
int totalExpected;
int totalRead = 0;
byte[] buf = new byte[8 * 1024]; public MultipartInputStreamHandler(ServletInputStream in,
int totalExpected) {
this.in = in;
this.totalExpected = totalExpected;
} // Reads the next line of input. Returns null to indicate the end
// of stream.
//
public String readLine() throws IOException {
StringBuffer sbuf = new StringBuffer();
int result;
String line; do {
result = this.readLine(buf, 0, buf.length); // this.readLine() does +=
if (result != -1) {
sbuf.append(new String(buf, 0, result)); //has edited by lzl
}
} while (result == buf.length); // loop only if the buffer was filled if (sbuf.length() == 0) {
return null; // nothing read, must be at the end of stream
} sbuf.setLength(sbuf.length() - 2); // cut off the trailing \r\n
return sbuf.toString();
} // A pass-through to ServletInputStream.readLine() that keeps track
// of how many bytes have been read and stops reading when the
// Content-Length limit has been reached.
//
public int readLine(byte b[], int off, int len) throws IOException {
if (totalRead >= totalExpected) {
return -1;
}
else {
if (len > (totalExpected - totalRead)) {
len = totalExpected - totalRead; // keep from reading off end
}
int result = in.readLine(b, off, len);
if (result > 0) {
totalRead += result;
}
return result;
}
}
}
// Class to filters MacBinary files to normal files on the fly
// Optimized for speed more than readability
class MacBinaryDecoderOutputStream extends FilterOutputStream { int bytesFiltered = 0;
int dataForkLength = 0; public MacBinaryDecoderOutputStream(OutputStream out) {
super(out);
} public void write(int b) throws IOException {
// Bytes 83 through 86 are a long representing the data fork length
// Check <= 86 first to short circuit early in the common case
if (bytesFiltered <= 86 && bytesFiltered >= 83) {
int leftShift = (86 - bytesFiltered) * 8;
dataForkLength = dataForkLength | (b & 0xff) << leftShift;
}
// Bytes 128 up to (128 + dataForkLength - 1) are the data fork
else if (bytesFiltered < (128 + dataForkLength) && bytesFiltered >= 128) {
out.write(b);
}
bytesFiltered++;
} public void write(byte b[]) throws IOException {
write(b, 0, b.length);
} public void write(byte b[], int off, int len) throws IOException {
// If the write is for content past the end of the data fork, ignore
if (bytesFiltered >= (128 + dataForkLength)) {
bytesFiltered += len;
}
// If the write is entirely within the data fork, write it directly
else if (bytesFiltered >= 128 &&
(bytesFiltered + len) <= (128 + dataForkLength)) {
out.write(b, off, len);
bytesFiltered += len;
}
// Otherwise, do the write a byte at a time to get the logic above
else {
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
}
}
// Copyright (C) 1998 by Jason Hunter <[email protected]>. All rights reserved.
// Use of this class is limited. Please see the LICENSE for more information.package com.oreilly.servlet;import java.io.*;
import java.util.*;
import javax.servlet.*;
import java.lang.*;
import javax.servlet.http.*;/**
* A utility class to handle <tt>multipart/form-data</tt> requests,
* the kind of requests that support file uploads. This class can
* receive arbitrarily large files (up to an artificial limit you can set),
* and fairly efficiently too.
* It cannot handle nested data (multipart content within multipart content)
* or internationalized content (such as non Latin-1 filenames).
* <p>
* It's used like this:
* <blockquote><pre>
* MultipartRequest multi = new MultipartRequest(req, ".");
*
* out.println("Params:");
* Enumeration params = multi.getParameterNames();
* while (params.hasMoreElements()) {
* String name = (String)params.nextElement();
* String value = multi.getParameter(name);
* out.println(name + " = " + value);
* }
* out.println();
*
* out.println("Files:");
* Enumeration files = multi.getFileNames();
* while (files.hasMoreElements()) {
* String name = (String)files.nextElement();
* String filename = multi.getFilesystemName(name);
* String type = multi.getContentType(name);
* File f = multi.getFile(name);
* out.println("name: " + name);
* out.println("filename: " + filename);
* out.println("type: " + type);
* if (f != null) {
* out.println("f.toString(): " + f.toString());
* out.println("f.getName(): " + f.getName());
* out.println("f.exists(): " + f.exists());
* out.println("f.length(): " + f.length());
* out.println();
* }
* }
* </pre></blockquote>
*
* A client can upload files using an HTML form with the following structure.
* Note that not all browsers support file uploads.
* <blockquote><pre>
* <FORM ACTION="/servlet/Handler" METHOD=POST
* ENCTYPE="multipart/form-data">
* What is your name? <INPUT TYPE=TEXT NAME=submitter> <BR>
* Which file to upload? <INPUT TYPE=FILE NAME=file> <BR>
* <INPUT TYPE=SUBMIT>
* </FORM>
* </pre></blockquote>
* <p>
* The full file upload specification is contained in experimental RFC 1867,
* available at <a href="http://www.ietf.org/rfc/rfc1867.txt">
* http://www.ietf.org/rfc/rfc1867.txt</a>.
*
* @author <b>Jason Hunter</b>, Copyright © 1998-1999
* @version 1.6, 00/03/19, better WebSphere 2.x/3.x content type workaround
* @version 1.5, 00/02/04, added auto MacBinary decoding for IE on Mac
* @version 1.4, 00/01/05, added getParameterValues(),
* WebSphere 2.x getContentType() workaround,
* stopped writing empty "unknown" file
* @version 1.3, 99/12/28, IE4 on Win98 lastIndexOf("boundary=") workaround
* @version 1.2, 99/12/20, IE4 on Mac readNextPart() workaround
* @version 1.1, 99/01/15, JSDK readLine() bug workaround
* @version 1.0, 98/09/18
*/
public class MultipartRequest { private static final int DEFAULT_MAX_POST_SIZE = 1024 * 1024 * 10; // 1 Meg
private static final String NO_FILE = "unknown"; private HttpServletRequest req;
private File dir=null;
private int maxSize;
private String tempFilename; //add by lzl
private boolean defaultFileFlag=false; //add by lzl
private int fileNo=1; //add by lzl
private Hashtable fileFullPaths; // add by wguzgg
private Hashtable fileLengths; //add by wguzgg
private Hashtable fileLocalPaths; // add by wguzgg
private Hashtable parameters = new Hashtable(); // name - Vector of values
private Hashtable files = new Hashtable(); // name - UploadedFile
//add by wguzgg
public long getFileLengths(String fn) {
Long fl=(Long)fileLengths.get(fn);
return fl.longValue();
} //add by wguzgg
public String getFileFullPath(String fn) {
String fp=(String)fileFullPaths.get(fn);
return fp;
} //add by wguzgg
public String getFileLocalPath(String fn) {
String flp=(String)fileLocalPaths.get(fn);
return flp;
} //add by lzl
public MultipartRequest(HttpServletRequest request,String saveDirectory,boolean fileFlag) throws IOException {
this(request, saveDirectory, DEFAULT_MAX_POST_SIZE,fileFlag);
} /**
* Constructs a new MultipartRequest to handle the specified request,
* saving any uploaded files to the given directory, and limiting the
* upload size to 1 Megabyte. If the content is too large, an
* IOException is thrown. This constructor actually parses the
* <tt>multipart/form-data</tt> and throws an IOException if there's any
* problem reading or parsing the request.
*
* @param request the servlet request
* @param saveDirectory the directory in which to save any uploaded files
* @exception IOException if the uploaded content is larger than 1 Megabyte
* or there's a problem reading or parsing the request
*/
public MultipartRequest(HttpServletRequest request,
String saveDirectory) throws IOException {
this(request, saveDirectory, DEFAULT_MAX_POST_SIZE);
} /**
* Constructs a new MultipartRequest to handle the specified request,
* saving any uploaded files to the given directory, and limiting the
* upload size to the specified length. If the content is too large, an
* IOException is thrown. This constructor actually parses the
* <tt>multipart/form-data</tt> and throws an IOException if there's any
* problem reading or parsing the request.
*
* @param request the servlet request
* @param saveDirectory the directory in which to save any uploaded files
* @param maxPostSize the maximum size of the POST content
* @exception IOException if the uploaded content is larger than
* <tt>maxPostSize</tt> or there's a problem reading or parsing the request
*/
public MultipartRequest(HttpServletRequest request,
String saveDirectory,
int maxPostSize) throws IOException {
// Sanity check values
if (request == null)
throw new IllegalArgumentException("request cannot be null");
if (saveDirectory == null)
throw new IllegalArgumentException("saveDirectory cannot be null");
if (maxPostSize <= 0) {
throw new IllegalArgumentException("maxPostSize must be positive");
} // Save the request, dir, and max size
req = request;
dir = new File(saveDirectory);
maxSize = maxPostSize;
tempFilename=saveDirectory.substring(saveDirectory.lastIndexOf("/"));
// Check saveDirectory is truly a directory
if (!dir.isDirectory())
throw new IllegalArgumentException("Not a directory: " + saveDirectory); // Check saveDirectory is writable
if (!dir.canWrite())
throw new IllegalArgumentException("Not writable: " + saveDirectory); // Now parse the request saving data to "parameters" and "files";
// write the file contents to the saveDirectory
readRequest();
} //add by lzl
public MultipartRequest(HttpServletRequest request,
String saveDirectory,
int maxPostSize,boolean fileFlag) throws IOException {
// Sanity check values
if (request == null)
throw new IllegalArgumentException("request cannot be null");
if (saveDirectory == null)
throw new IllegalArgumentException("saveDirectory cannot be null");
if (maxPostSize <= 0) {
throw new IllegalArgumentException("maxPostSize must be positive");
} // Save the request, dir, and max size
req = request;
dir = new File(saveDirectory);
maxSize = maxPostSize;
tempFilename=saveDirectory.substring(saveDirectory.lastIndexOf("/"));
defaultFileFlag=fileFlag;
// Check saveDirectory is truly a directory
if (!dir.isDirectory())
throw new IllegalArgumentException("Not a directory: " + saveDirectory); // Check saveDirectory is writable
if (!dir.canWrite())
throw new IllegalArgumentException("Not writable: " + saveDirectory); // Now parse the request saving data to "parameters" and "files";
// write the file contents to the saveDirectory
readRequest();
}
/**
* Constructor with an old signature, kept for backward compatibility.
* Without this constructor, a servlet compiled against a previous version
* of this class (pre 1.4) would have to be recompiled to link with this
* version. This constructor supports the linking via the old signature.
* Callers must simply be careful to pass in an HttpServletRequest.
*
*/
public MultipartRequest(ServletRequest request,
String saveDirectory) throws IOException {
this((HttpServletRequest)request, saveDirectory);
} /**
* Constructor with an old signature, kept for backward compatibility.
* Without this constructor, a servlet compiled against a previous version
* of this class (pre 1.4) would have to be recompiled to link with this
* version. This constructor supports the linking via the old signature.
* Callers must simply be careful to pass in an HttpServletRequest.
*
*/
public MultipartRequest(ServletRequest request,
String saveDirectory,
int maxPostSize) throws IOException {
this((HttpServletRequest)request, saveDirectory, maxPostSize);
} /**
* Returns the names of all the parameters as an Enumeration of
* Strings. It returns an empty Enumeration if there are no parameters.
*
* @return the names of all the parameters as an Enumeration of Strings
*/
public Enumeration getParameterNames() {
return parameters.keys();
} /**
* Returns the names of all the uploaded files as an Enumeration of
* Strings. It returns an empty Enumeration if there are no uploaded
* files. Each file name is the name specified by the form, not by
* the user.
*
* @return the names of all the uploaded files as an Enumeration of Strings
*/
public Enumeration getFileNames() {
return files.keys();
} /**
* Returns the value of the named parameter as a String, or null if
* the parameter was not sent or was sent without a value. The value
* is guaranteed to be in its normal, decoded form. If the parameter
* has multiple values, only the last one is returned (for backward
* compatibility). For parameters with multiple values, it's possible
* the last "value" may be null.
*
* @param name the parameter name
* @return the parameter value
*/
public String getParameter(String name) {
try {
Vector values = (Vector)parameters.get(name);
if (values == null || values.size() == 0) {
return null;
}
String value = (String)values.elementAt(values.size() - 1);
return value;
}
catch (Exception e) {
return null;
}
} /**
* Returns the values of the named parameter as a String array, or null if
* the parameter was not sent. The array has one entry for each parameter
* field sent. If any field was sent without a value that entry is stored
* in the array as a null. The values are guaranteed to be in their
* normal, decoded form. A single value is returned as a one-element array.
*
* @param name the parameter name
* @return the parameter values
*/
public String[] getParameterValues(String name) {
try {
Vector values = (Vector)parameters.get(name);
if (values == null || values.size() == 0) {
return null;
}
String[] valuesArray = new String[values.size()];
values.copyInto(valuesArray);
return valuesArray;
}
catch (Exception e) {
return null;
}
} /**
* Returns the filesystem name of the specified file, or null if the
* file was not included in the upload. A filesystem name is the name
* specified by the user. It is also the name under which the file is
* actually saved.
*
* @param name the file name
* @return the filesystem name of the file
*/
public String getFilesystemName(String name) {
try {
UploadedFile file = (UploadedFile)files.get(name);
return file.getFilesystemName(); // may be null
}
catch (Exception e) {
return null;
}
} /**
* Returns the content type of the specified file (as supplied by the
* client browser), or null if the file was not included in the upload.
*
* @param name the file name
* @return the content type of the file
*/
public String getContentType(String name) {
try {
UploadedFile file = (UploadedFile)files.get(name);
return file.getContentType(); // may be null
}
catch (Exception e) {
return null;
}
} /**
* Returns a File object for the specified file saved on the server's
* filesystem, or null if the file was not included in the upload.
*
* @param name the file name
* @return a File object for the named file
*/
public File getFile(String name) {
try {
UploadedFile file = (UploadedFile)files.get(name);
return file.getFile(); // may be null
}
catch (Exception e) {
return null;
}
} /**
* The workhorse method that actually parses the request. A subclass
* can override this method for a better optimized or differently
* behaved implementation.
*
* @exception IOException if the uploaded content is larger than
* <tt>maxSize</tt> or there's a problem parsing the request
*/
protected void readRequest() throws IOException {
// Check the content length to prevent denial of service attacks
int length = req.getContentLength();
if (length > maxSize) {
throw new IOException("Posted content length of " + length +
" exceeds limit of " + maxSize);
} // Check the content type to make sure it's "multipart/form-data"
// Access header two ways to work around WebSphere oddities
String type = null;
String type1 = req.getHeader("Content-Type");
String type2 = req.getContentType();
// If one value is null, choose the other value
if (type1 == null && type2 != null) {
type = type2;
}
else if (type2 == null && type1 != null) {
type = type1;
}
// If neither value is null, choose the longer value
else if (type1 != null && type2 != null) {
type = (type1.length() > type2.length() ? type1 : type2);
} if (type == null ||
!type.toLowerCase().startsWith("multipart/form-data")) {
throw new IOException("Posted content type isn't multipart/form-data");
}
// Get the boundary string; it's included in the content type.
// Should look something like "------------------------12012133613061"
String boundary = extractBoundary(type);
if (boundary == null) {
throw new IOException("Separation boundary was not specified");
} // Construct the special input stream we'll read from
MultipartInputStreamHandler in =
new MultipartInputStreamHandler(req.getInputStream(), length); // Read the first line, should be the first boundary
String line = in.readLine();
if (line == null) {
throw new IOException("Corrupt form data: premature ending");
} // Verify that the line is the boundary
if (!line.startsWith(boundary)) {
throw new IOException("Corrupt form data: no leading boundary");
} // Now that we're just beyond the first boundary, loop over each part
boolean done = false;
while (!done) {
done = readNextPart(in, boundary);
}
} /**
* A utility method that reads an individual part. Dispatches to
* readParameter() and readAndSaveFile() to do the actual work. A
* subclass can override this method for a better optimized or
* differently behaved implementation.
*
* @param in the stream from which to read the part
* @param boundary the boundary separating parts
* @return a flag indicating whether this is the last part
* @exception IOException if there's a problem reading or parsing the
* request
*
* @see readParameter
* @see readAndSaveFile
*/
protected boolean readNextPart(MultipartInputStreamHandler in,
String boundary) throws IOException {
// Read the first line, should look like this:
// content-disposition: form-data; name="field1"; filename="file1.txt"
String line = in.readLine();
if (line == null) {
// No parts left, we're done
return true;
}
else if (line.length() == 0) {
// IE4 on Mac sends an empty line at the end; treat that as the end.
// Thanks to Daniel Lemire and Henri Tourigny for this fix.
return true;
} // Parse the content-disposition line
String[] dispInfo = extractDispositionInfo(line);
String disposition = dispInfo[0];
String name = dispInfo[1];
String filename = dispInfo[2]; // Now onto the next line. This will either be empty
// or contain a Content-Type and then an empty line.
line = in.readLine();
if (line == null) {
// No parts left, we're done
return true;
} // Get the content type, or null if none specified
String contentType = extractContentType(line);
if (contentType != null) {
// Eat the empty line
line = in.readLine();
if (line == null || line.length() > 0) { // line should be empty
throw new
IOException("Malformed line after content type: " + line);
}
}
else {
// Assume a default content type
contentType = "application/octet-stream";
} // Now, finally, we read the content (end after reading the boundary)
if (filename == null) {
// This is a parameter, add it to the vector of values
String value = readParameter(in, boundary);
if (value.equals("")) {
value = null; // treat empty strings like nulls
}
Vector existingValues = (Vector)parameters.get(name);
if (existingValues == null) {
existingValues = new Vector();
parameters.put(name, existingValues);
}
existingValues.addElement(value);
}
else {
// This is a file
readAndSaveFile(in, boundary, filename, contentType,name);
if (fileLocalPaths==null) //add by wguzgg
fileLocalPaths=new Hashtable(); //add by wguzgg
fileLocalPaths.put(new String(filename),dir.getAbsolutePath()); //add by wguzgg
if (fileLengths==null) //add by wguzgg
fileLengths=new Hashtable(); //add by wguzgg
File f = new File(dir + File.separator + filename); //add by wguzgg
Long fileLength=new Long(f.length()); //add by wguzgg
fileLengths.put(new String(filename),fileLength); //add by wguzgg
if (filename.equals(NO_FILE)) {
files.put(name, new UploadedFile(null, null, null));
}
else {
if(fileNo==1 && !defaultFileFlag){
String fileType=filename.substring(filename.indexOf("."));
filename=""+tempFilename+fileType.trim();
}
files.put(name,new UploadedFile(dir.toString(), filename, contentType));
fileNo++;
}
}
return false; // there's more to read
} /**
* A utility method that reads a single part of the multipart request
* that represents a parameter. A subclass can override this method
* for a better optimized or differently behaved implementation.
*
* @param in the stream from which to read the parameter information
* @param boundary the boundary signifying the end of this part
* @return the parameter value
* @exception IOException if there's a problem reading or parsing the
* request
*/
protected String readParameter(MultipartInputStreamHandler in,
String boundary) throws IOException {
StringBuffer sbuf = new StringBuffer();
String line; while ((line = in.readLine()) != null) {
if (line.startsWith(boundary)) break;
sbuf.append(line + "\r\n"); // add the \r\n in case there are many lines
} if (sbuf.length() == 0) {
return null; // nothing read
} sbuf.setLength(sbuf.length() - 2); // cut off the last line's \r\n
return sbuf.toString(); // no URL decoding needed
} /**
* A utility method that reads a single part of the multipart request
* that represents a file, and saves the file to the given directory.
* A subclass can override this method for a better optimized or
* differently behaved implementation.
*
* @param in the stream from which to read the file
* @param boundary the boundary signifying the end of this part
* @param dir the directory in which to save the uploaded file
* @param filename the name under which to save the uploaded file
* @exception IOException if there's a problem reading or parsing the
* request
*/
protected void readAndSaveFile(MultipartInputStreamHandler in,
String boundary,
String filename,
String contentType,
String name) throws IOException {
OutputStream out = null;
// A filename of NO_FILE means no file was sent, so just read to the
// next boundary and ignore the empty contents
if (filename.equals(NO_FILE)) {
out = new ByteArrayOutputStream(); // write to nowhere
}
// A MacBinary file goes through a decoder
else if (contentType.equals("application/x-macbinary")){
File f = new File(dir + File.separator + filename);
out = new MacBinaryDecoderOutputStream(
new BufferedOutputStream(
new FileOutputStream(f), 8 * 1024));
}
// A real file's contents are written to disk
else {
if(fileNo==1 && !defaultFileFlag){
String fileType=filename.substring(filename.indexOf("."));
filename=""+tempFilename+fileType.trim();
}
File f = new File(dir + File.separator + filename);
out = new BufferedOutputStream(new FileOutputStream(f), 8 * 1024);
} byte[] bbuf = new byte[100 * 1024]; // 100K
int result;
String line; // ServletInputStream.readLine() has the annoying habit of
// adding a \r\n to the end of the last line.
// Since we want a byte-for-byte transfer, we have to cut those chars.
boolean rnflag = false;
while ((result = in.readLine(bbuf, 0, bbuf.length)) != -1) {
// Check for boundary
if (result > 2 && bbuf[0] == '-' && bbuf[1] == '-') { // quick pre-check
line = new String(bbuf, 0, result); //has edited by lzl
if (line.startsWith(boundary)) break;
}
// Are we supposed to write \r\n for the last iteration?
if (rnflag) {
out.write('\r'); out.write('\n');
rnflag = false;
}
// Write the buffer, postpone any ending \r\n
if (result >= 2 &&
bbuf[result - 2] == '\r' &&
bbuf[result - 1] == '\n') {
out.write(bbuf, 0, result - 2); // skip the last 2 chars
rnflag = true; // make a note to write them on the next iteration
}
else {
out.write(bbuf, 0, result);
}
}
out.flush();
out.close();
} // Extracts and returns the boundary token from a line.
//
private String extractBoundary(String line) {
// Use lastIndexOf() because IE 4.01 on Win98 has been known to send the
// "boundary=" string multiple times. Thanks to David Wall for this fix.
int index = line.lastIndexOf("boundary=");
if (index == -1) {
return null;
}
String boundary = line.substring(index + 9); // 9 for "boundary=" // The real boundary is always preceeded by an extra "--"
boundary = "--" + boundary; return boundary;
} // Extracts and returns disposition info from a line, as a String array
// with elements: disposition, name, filename. Throws an IOException
// if the line is malformatted.
//
private String[] extractDispositionInfo(String line) throws IOException {
// Return the line's data as an array: disposition, name, filename
String[] retval = new String[3]; // Convert the line to a lowercase string without the ending \r\n
// Keep the original line for error messages and for variable names.
String origline = line;
line = origline.toLowerCase(); // Get the content disposition, should be "form-data"
int start = line.indexOf("content-disposition: ");
int end = line.indexOf(";");
if (start == -1 || end == -1) {
throw new IOException("Content disposition corrupt: " + origline);
}
String disposition = line.substring(start + 21, end);
if (!disposition.equals("form-data")) {
throw new IOException("Invalid content disposition: " + disposition);
} // Get the field name
start = line.indexOf("name=\"", end); // start at last semicolon
end = line.indexOf("\"", start + 7); // skip name=\"
if (start == -1 || end == -1) {
throw new IOException("Content disposition corrupt: " + origline);
}
String name = origline.substring(start + 6, end); // Get the filename, if given
String filename = null;
start = line.indexOf("filename=\"", end + 2); // start after name
end = line.indexOf("\"", start + 10); // skip filename=\"
if (start != -1 && end != -1) { // note the !=
filename = origline.substring(start + 10, end);
String temp=new String(filename); //add by wguzgg
// The filename may contain a full path. Cut to just the filename.
int slash =
Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
if (slash > -1) {
filename = filename.substring(slash + 1); // past last slash
}
if (filename.equals("")) filename = NO_FILE; // sanity check
if (fileFullPaths==null) //add by wguzgg
fileFullPaths=new Hashtable(); //add by wguzgg
fileFullPaths.put(new String(filename),temp); //add by wguzgg
} // Return a String array: disposition, name, filename
retval[0] = disposition;
retval[1] = name;
retval[2] = filename;
return retval;
} // Extracts and returns the content type from a line, or null if the
// line was empty. Throws an IOException if the line is malformatted.
//
private String extractContentType(String line) throws IOException {
String contentType = null; // Convert the line to a lowercase string
String origline = line;
line = origline.toLowerCase(); // Get the content type, if any
if (line.startsWith("content-type")) {
int start = line.indexOf(" ");
if (start == -1) {
throw new IOException("Content type corrupt: " + origline);
}
contentType = line.substring(start + 1);
}
else if (line.length() != 0) { // no content type, so should be empty
throw new IOException("Malformed line after disposition: " + origline);
} return contentType;
}
}
// A class to hold information about an uploaded file.
//
class UploadedFile { private String dir;
private String filename;
private String type; UploadedFile(String dir, String filename, String type) {
this.dir = dir;
this.filename = filename;
this.type = type;
} public String getContentType() {
return type;
} public String getFilesystemName() {
return filename;
} public File getFile() {
if (dir == null || filename == null) {
return null;
}
else {
return new File(dir + File.separator + filename);
}
}
}
// A class to aid in reading multipart/form-data from a ServletInputStream.
// It keeps track of how many bytes have been read and detects when the
// Content-Length limit has been reached. This is necessary since some
// servlet engines are slow to notice the end of stream.
//
// Mac users: The Mac doesn't like class names which exceed 32 characters
// (including the ".class") so while this class is usable from a JAR
// anywhere, it won't compile on a Mac.
//
class MultipartInputStreamHandler { ServletInputStream in;
int totalExpected;
int totalRead = 0;
byte[] buf = new byte[8 * 1024]; public MultipartInputStreamHandler(ServletInputStream in,
int totalExpected) {
this.in = in;
this.totalExpected = totalExpected;
} // Reads the next line of input. Returns null to indicate the end
// of stream.
//
public String readLine() throws IOException {
StringBuffer sbuf = new StringBuffer();
int result;
String line; do {
result = this.readLine(buf, 0, buf.length); // this.readLine() does +=
if (result != -1) {
sbuf.append(new String(buf, 0, result)); //has edited by lzl
}
} while (result == buf.length); // loop only if the buffer was filled if (sbuf.length() == 0) {
return null; // nothing read, must be at the end of stream
} sbuf.setLength(sbuf.length() - 2); // cut off the trailing \r\n
return sbuf.toString();
} // A pass-through to ServletInputStream.readLine() that keeps track
// of how many bytes have been read and stops reading when the
// Content-Length limit has been reached.
//
public int readLine(byte b[], int off, int len) throws IOException {
if (totalRead >= totalExpected) {
return -1;
}
else {
if (len > (totalExpected - totalRead)) {
len = totalExpected - totalRead; // keep from reading off end
}
int result = in.readLine(b, off, len);
if (result > 0) {
totalRead += result;
}
return result;
}
}
}
// Class to filters MacBinary files to normal files on the fly
// Optimized for speed more than readability
class MacBinaryDecoderOutputStream extends FilterOutputStream { int bytesFiltered = 0;
int dataForkLength = 0; public MacBinaryDecoderOutputStream(OutputStream out) {
super(out);
} public void write(int b) throws IOException {
// Bytes 83 through 86 are a long representing the data fork length
// Check <= 86 first to short circuit early in the common case
if (bytesFiltered <= 86 && bytesFiltered >= 83) {
int leftShift = (86 - bytesFiltered) * 8;
dataForkLength = dataForkLength | (b & 0xff) << leftShift;
}
// Bytes 128 up to (128 + dataForkLength - 1) are the data fork
else if (bytesFiltered < (128 + dataForkLength) && bytesFiltered >= 128) {
out.write(b);
}
bytesFiltered++;
} public void write(byte b[]) throws IOException {
write(b, 0, b.length);
} public void write(byte b[], int off, int len) throws IOException {
// If the write is for content past the end of the data fork, ignore
if (bytesFiltered >= (128 + dataForkLength)) {
bytesFiltered += len;
}
// If the write is entirely within the data fork, write it directly
else if (bytesFiltered >= 128 &&
(bytesFiltered + len) <= (128 + dataForkLength)) {
out.write(b, off, len);
bytesFiltered += len;
}
// Otherwise, do the write a byte at a time to get the logic above
else {
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
}
}
请问您的代码是自己写的还是开放的源码?我从那里可以获得更多的资料呢?
还有我想从页面获得一些个人信息,比如是谁在上载,这样的数据应该如何传递?
好像页面如果指定了ENCTYPE="multipart/form-data"的话,直接写的表单就不能传递了
* Description: A Thread safe class for parsing a Servlet's InputStream. It is especially usefull when the HTML form that
* the servlet is processing is of multipart/form-data
* Copyright: Copyright (c) Dane S. Foster<p>
* @author Dane S. Foster <mailto:[email protected]>
* @version 1.1
*/
import java.io.*;
import java.util.*;
import javax.servlet.*;
public class MultipartFormReader
{
private final int READY = 0;
private final int FILENAME = 1;
private final int NAME = 2;
private final int BINDATA = 3;
private final int TXTDATA = 4; /**
* Parses the ServletInputStream of encoding multipart/form-data and separates it into name value pairs. The name-value pairs
* are stored in the <code>map</code> argument. There are a couple of this to be aware of. This class is not a replacement for
* the ServletRequest class but augments it. It should only be used in the case where the client is issuing an HTTP POST request
* and the form-encoding is multipart/form-data.
*
* <p>In the event the HTTP POST request contains binary data --for example, when someone
* wants to upload data to your servlet-- it is stored as a byte array. You can retrieve the binary data by calling the get method
* on the <code>map</code> object you passed in as a parameter.
* <p>i.e.: byte[] uploadedImage = (byte[]) map.get( "fieldname" );<p>
* There is no limit to the amount of binary data that can be uploaded and retrieved.
*
* <p>For those situations where the HTTP POST request contains list data (i.e. checkboxes, multiple selection lists), the list data
* is stored in a <code>java.util.List</code> object. This is equivalent to the <code>javax.servlet.ServletRequest</code>'s
* <code>public String[] getParameterValues( java.lang.String )</code> method.
*<p>i.e.: java.util.List checkboxItems = (java.util.List)map.get( "fieldname" );
*
* @param request A <code>ServletRequest</code> object.
* @param map The <code>Map</code> will be populated with the name value pairs of the HTML form's content.
* It works pretty much like the <code>HttpServletRequest</code> class except it can handle multipart/form-data.
*/
public void read( ServletRequest request, Map map ) throws IOException
{
byte[] bytes = new byte[ 2048 ];
int state = 0;
int eof = 0; /* Get the separator for this request's form data. It is necessary for parsing this request's form data. */
String boundary = request.getContentType();
int pos = boundary.indexOf( "=" );
boundary = boundary.substring( pos + 1 );
boundary = "--" + boundary; String fieldName = null;
String fieldValue = null; ServletInputStream sStream = request.getInputStream(); ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream(); eof = sStream.readLine( bytes, 0, 2048 ); map.clear();
state = 0; while( -1 != eof )
{
String filter = new String( bytes, 0, eof ); if( filter.startsWith( boundary ) )
{
state = READY; if( null != fieldName )
{
if( null != fieldValue )
{
Object o = map.get( fieldName );
Object val = fieldValue.substring( 0, fieldValue.length() - 2 );
if( null == o )
map.put( fieldName, val );
else
{
if( o instanceof List )
((List)o).add( val );
else
{
List list = new ArrayList();
list.add( o );
list.add( val );
map.put( fieldName, list );
}
}
}
else if( dataBuffer.size() > 2 )
{
map.put( fieldName, dataBuffer.toByteArray() );
} fieldName = null;
fieldValue = null;
dataBuffer.reset();
}
}
else if( filter.startsWith( "Content-Disposition: form-data" ) && state == READY )
{
StringTokenizer tokenizer = new StringTokenizer( filter, ";=\"" ); while( tokenizer.hasMoreTokens() )
{
String token = tokenizer.nextToken().trim(); if( token.startsWith( "name" ) )
{
fieldName = tokenizer.nextToken().trim();
state = NAME;
}
else if( token.startsWith( "filename" ) )
{
state = FILENAME;
break;
}
}
}
else if( filter.equals( "\r\n" ) && state == FILENAME ) state = BINDATA;
else if( filter.equals("\r\n" ) && state == NAME ) state = TXTDATA;
else if( state == TXTDATA ) fieldValue = fieldValue == null ? filter : fieldValue + filter;
else if( state == BINDATA ) dataBuffer.write( bytes, 0, eof ); eof = sStream.readLine( bytes, 0, 2048 );
}// Parsing stops here. The Map should now contain all of the form's data.
sStream.close();
} /**
* A utility method that saves you the trouble of having to create a Map object and passing it to the other read method.
*
* @param request The ServletRequest object
*
* @return A java.util.HashMap containing the name-value pairs of the HTTP POST's form data
*/
public HashMap read( ServletRequest request ) throws IOException
{
HashMap hash = new HashMap();
this.read( request, hash );
return hash;
}
}