⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 multipartparser.java

📁 该HttpProxy用于从Applet或Swing界面中访问Ejb和服务端的JavaBean。
💻 JAVA
字号:
// Copyright (C) 1998-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
// All rights reserved.  Use of this class is limited.
// Please see the LICENSE for more information.

package com.nari.pmos.arch.multipart;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletInputStream;


public class MultipartParser {

	/** input stream to read parts from */
	private ServletInputStream in;

	/** MIME boundary that delimits parts */
	private String boundary;

	/** reference to the last file part we returned */
	private FilePart lastFilePart;

	/** buffer for readLine method */
	private byte[] buf = new byte[8 * 1024];

	/** default encoding */
	private static String DEFAULT_ENCODING = "ISO-8859-1";

	/** preferred encoding */
	private String encoding = DEFAULT_ENCODING;
	
	private static final int DEFAULT_MAX_POST_SIZE = 10 * 10 * 1024 * 1024;

	public MultipartParser(HttpServletRequest req)
			throws IOException {
		this(req, DEFAULT_MAX_POST_SIZE, true, true);
	}

	public MultipartParser(HttpServletRequest req, int maxSize, boolean buffer,
			boolean limitLength) throws IOException {
		this(req, maxSize, buffer, limitLength, null);
	}

	public MultipartParser(HttpServletRequest req, int maxSize, boolean buffer,
			boolean limitLength, String encoding) throws IOException {

		if (encoding != null) {
			setEncoding(encoding);
		}

		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");
		}

		// 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);
		}

		// 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");
		}

		ServletInputStream in = req.getInputStream();

		// If required, wrap the real input stream with classes that
		// "enhance" its behaviour for performance and stability
		if (buffer) {
			in = new BufferedServletInputStream(in);
		}
		if (limitLength) {
			in = new LimitedServletInputStream(in, length);
		}

		// Save our values for later
		this.in = in;
		this.boundary = boundary;

		// Read until we hit the boundary
		// Some clients send a preamble (per RFC 2046), so ignore that
		// Thanks to Ben Johnson, ben.johnson@merrillcorp.com, for pointing out
		// the need for preamble support.
		do {
			String line = readLine();
			if (line == null) {
				throw new IOException("Corrupt form data: premature ending");
			}
			// See if this line is the boundary, and if so break
			if (line.startsWith(boundary)) {
				break; // success
			}
		} while (true);
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	public Part readNextPart() throws IOException {
		// Make sure the last file was entirely read from the input
		if (lastFilePart != null) {
			lastFilePart.getInputStream().close();
			lastFilePart = null;
		}

		// Read the headers; they look like this (not all may be present):
		// Content-Disposition: form-data; name="field1"; filename="file1.txt"
		// Content-Type: type/subtype
		// Content-Transfer-Encoding: binary
		Vector headers = new Vector();

		String line = readLine();
		if (line == null) {
			// No parts left, we're done
			return null;
		} 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 null;
		}

		// Read the following header lines we hit an empty line
		// A line starting with whitespace is considered a continuation;
		// that requires a little special logic. Thanks to Nic Ferrier for
		// identifying a good fix.
		while (line != null && line.length() > 0) {
			String nextLine = null;
			boolean getNextLine = true;
			while (getNextLine) {
				nextLine = readLine();
				if (nextLine != null
						&& (nextLine.startsWith(" ") || nextLine
								.startsWith("\t"))) {
					line = line + nextLine;
				} else {
					getNextLine = false;
				}
			}
			// Add the line to the header list
			headers.addElement(line);
			line = nextLine;
		}

		// If we got a null above, it's the end
		if (line == null) {
			return null;
		}

		String name = null;
		String filename = null;
		String origname = null;
		String contentType = "text/plain"; // rfc1867 says this is the default

		Enumeration em = headers.elements();
		while (em.hasMoreElements()) {
			String headerline = (String) em.nextElement();
			if (headerline.toLowerCase().startsWith("content-disposition:")) {
				// Parse the content-disposition line
				String[] dispInfo = extractDispositionInfo(headerline);
				// String disposition = dispInfo[0]; // not currently used
				name = dispInfo[1];
				filename = dispInfo[2];
				origname = dispInfo[3];
			} else if (headerline.toLowerCase().startsWith("content-type:")) {
				// Get the content type, or null if none specified
				String type = extractContentType(headerline);
				if (type != null) {
					contentType = type;
				}
			}
		}

		// 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
			// The encoding is needed to help parse the value
			return new ParamPart(name, in, boundary, encoding);
		} else {
			// This is a file
			if (filename.equals("")) {
				filename = null; // empty filename, probably an "empty" file
									// param
			}
			lastFilePart = new FilePart(name, in, boundary, contentType,
					filename, origname);
			return lastFilePart;
		}
	}

	/**
	 * Extracts and returns the boundary token from a line.
	 * 
	 * @return the boundary token.
	 */
	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="
		if (boundary.charAt(0) == '"') {
			// The boundary is enclosed in quotes, strip them
			index = boundary.lastIndexOf('"');
			boundary = boundary.substring(1, index);
		}

		// The real boundary is always preceeded by an extra "--"
		boundary = "--" + boundary;

		return boundary;
	}

	/**
	 * Extracts and returns disposition info from a line, as a
	 * <code>String<code>
	 * array with elements: disposition, name, filename.
	 * 
	 * @return String[] of elements: disposition, name, filename.
	 * @exception  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[4];

		// 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=\"
		int startOffset = 6;
		if (start == -1 || end == -1) {
			// Some browsers like lynx don't surround with ""
			// Thanks to Deon van der Merwe, dvdm@truteq.co.za, for noticing
			start = line.indexOf("name=", end);
			end = line.indexOf(";", start + 6);
			if (start == -1) {
				throw new IOException("Content disposition corrupt: "
						+ origline);
			} else if (end == -1) {
				end = line.length();
			}
			startOffset = 5; // without quotes we have one fewer char to skip
		}
		String name = origline.substring(start + startOffset, end);

		// Get the filename, if given
		String filename = null;
		String origname = 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);
			origname = filename;
			// 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
			}
		}

		// Return a String array: disposition, name, filename
		// empty filename denotes no file posted!
		retval[0] = disposition;
		retval[1] = name;
		retval[2] = filename;
		retval[3] = origname;
		return retval;
	}

	/**
	 * Extracts and returns the content type from a line, or null if the line
	 * was empty.
	 * 
	 * @return content type, or null if line was empty.
	 * @exception IOException
	 *                if the line is malformatted.
	 */
	private static String extractContentType(String line) throws IOException {
		// Convert the line to a lowercase string
		line = line.toLowerCase();

		// Get the content type, if any
		// Note that Opera at least puts extra info after the type, so handle
		// that. For example: Content-Type: text/plain; name="foo"
		// Thanks to Leon Poyyayil, leon.poyyayil@trivadis.com, for noticing
		// this.
		int end = line.indexOf(";");
		if (end == -1) {
			end = line.length();
		}

		return line.substring(13, end).trim(); // "content-type:" is 13
	}

	/**
	 * Read the next line of input.
	 * 
	 * @return a String containing the next line of input from the stream, or
	 *         null to indicate the end of the stream.
	 * @exception IOException
	 *                if an input or output exception has occurred.
	 */
	private String readLine() throws IOException {
		StringBuffer sbuf = new StringBuffer();
		int result;
		String line;

		do {
			result = in.readLine(buf, 0, buf.length); // does +=
			if (result != -1) {
				sbuf.append(new String(buf, 0, result, encoding));
			}
		} 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
		}

		// Cut off the trailing \n or \r\n
		// It should always be \r\n but IE5 sometimes does just \n
		// Thanks to Luke Blaikie for helping make this work with \n
		int len = sbuf.length();
		if (len >= 2 && sbuf.charAt(len - 2) == '\r') {
			sbuf.setLength(len - 2); // cut \r\n
		} else if (len >= 1 && sbuf.charAt(len - 1) == '\n') {
			sbuf.setLength(len - 1); // cut \n
		}
		return sbuf.toString();
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -