All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.blade.web.multipart.Multipart Maven / Gradle / Ivy

/**
 * Copyright (c) 2015, biezhi 王爵 ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.blade.web.multipart;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import blade.kit.IOKit;

/**
 * Multipart
 *
 * @author	biezhi
 * @since	1.0
 */
public class Multipart {

	public static final String CONTENT_TYPE = "Content-Type";

	public static final String CONTENT_DISPOSITION = "Content-Disposition";

	public static final String CONTENT_LENGTH = "Content-Length";

	public static final String FORM_DATA = "form-data";

	public static final String ATTACHMENT = "attachment";

	public static final String MULTIPART = "multipart/";

	public static final String MULTIPART_MIXED = "multipart/mixed";

	public static boolean isMultipartContent(HttpServletRequest request) {
		if (!"post".equals(request.getMethod().toLowerCase())) {
			return false;
		}

		String contentType = request.getContentType();
		if (contentType == null) {
			return false;
		}
		if (contentType.toLowerCase().startsWith(MULTIPART)) {
			return true;
		}

		return false;
	}

	public void parse(HttpServletRequest request, MultipartHandler partHandler) throws IOException, MultipartException {
		if (!isMultipartContent(request)) {
			throw new MultipartException("Not a multipart content. The HTTP method should be 'POST' and the " +
					"Content-Type 'multipart/form-data' or 'multipart/mixed'.");
		}

		InputStream inputStream = request.getInputStream();

		String contentType = request.getContentType();
		String charEncoding = request.getCharacterEncoding();

		byte[] boundary = getBoundary(contentType);
		if (boundary == null) {
			throw new MultipartException("the request was rejected because no multipart boundary was found");
		}

		// create a multipart reader
		MultipartReader multipartReader = new MultipartReader(inputStream, boundary);
		multipartReader.setHeaderEncoding(charEncoding);

		String currentFieldName = null;
		boolean skipPreamble = true;

		for (;;) {
			boolean nextPart;
			if (skipPreamble) {
				nextPart = multipartReader.skipPreamble();
			} else {
				nextPart = multipartReader.readBoundary();
			}
			if (!nextPart) {
				if (currentFieldName == null) {
					// outer multipart terminated -> no more data
					return;
				}
				// inner multipart terminated -> return to parsing the outer
				multipartReader.setBoundary(boundary);
				currentFieldName = null;
				continue;
			}

			String headersString = multipartReader.readHeaders();
			Map headers = getHeadersMap(headersString);

			if (currentFieldName == null) {

				// we're parsing the outer multipart
				String fieldName = getFieldName( headers.get(CONTENT_DISPOSITION) );
				if (fieldName != null) {

					String partContentType = headers.get(CONTENT_TYPE);
					if (partContentType != null &&  partContentType.toLowerCase().startsWith(MULTIPART_MIXED)) {

						// multiple files associated with this field name
						currentFieldName = fieldName;
						multipartReader.setBoundary( getBoundary(partContentType));
						skipPreamble = true;

						continue;
					}

					String fileName = getFileName( headers.get(CONTENT_DISPOSITION) );
					if (fileName == null) {
						// call the part handler
						String value = IOKit.toString(multipartReader.newInputStream());
						partHandler.handleFormItem(fieldName, value);
					} else {

						// create the temp file
						File tempFile = createTempFile(multipartReader);

						// call the part handler
						FileItem fileItem = new FileItem(fieldName, fileName, partContentType, tempFile.length(), tempFile, headers);
						partHandler.handleFileItem(fieldName, fileItem);
					}

					continue;
				}
			} else {
				String fileName = getFileName( headers.get(CONTENT_DISPOSITION) );
				String partContentType = headers.get(CONTENT_TYPE);
				if (fileName != null) {

					// create the temp file
					File tempFile = createTempFile(multipartReader);

					// call the part handler
					FileItem fileItem = new FileItem(currentFieldName, fileName, partContentType, tempFile.length(),
							tempFile, headers);
					partHandler.handleFileItem(currentFieldName, fileItem);
					continue;
				}
			}
			multipartReader.discardBodyData();
		}

	}

	private File createTempFile(MultipartReader multipartReader) throws IOException {
		File tempFile = File.createTempFile("com.blade.file_", null);
		FileOutputStream outputStream = null;
		try {
			outputStream = new FileOutputStream(tempFile);
			copy( multipartReader.newInputStream(), outputStream );
		} finally {
			if (outputStream != null) {
				try { outputStream.close(); } catch (Exception e) {}
			}
		}

		return tempFile;
	}

	private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
	private static final int EOF = -1;

	private long copy(InputStream input, OutputStream output) throws IOException {
		long count = 0;
		int n = 0;
		byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
		while (EOF != (n = input.read(buffer))) {
			output.write(buffer, 0, n);
			count += n;
		}
		return count;
	}

	protected Map getHeadersMap(String headerPart) {
		final int len = headerPart.length();
		final Map headers = new HashMap();

		int start = 0;
		for (;;) {
			int end = parseEndOfLine(headerPart, start);
			if (start == end) {
				break;
			}
			String header = headerPart.substring(start, end);
			start = end + 2;
			while (start < len) {
				int nonWs = start;
				while (nonWs < len) {
					char c = headerPart.charAt(nonWs);
					if (c != ' '  &&  c != '\t') {
						break;
					}
					++nonWs;
				}
				if (nonWs == start) {
					break;
				}
				// continuation line found
				end = parseEndOfLine(headerPart, nonWs);
				header += " " + headerPart.substring(nonWs, end);
				start = end + 2;
			}

			// parse header line
			final int colonOffset = header.indexOf(':');
			if (colonOffset == -1) {
				// this header line is malformed, skip it.
				continue;
			}
			String headerName = header.substring(0, colonOffset).trim();
			String headerValue = header.substring(header.indexOf(':') + 1).trim();

			if (headers.containsKey(headerName)) {
				headers.put( headerName, headers.get(headerName) + "," + headerValue );
			} else {
				headers.put(headerName, headerValue);
			}
		}

		return headers;
	}

	private int parseEndOfLine(String headerPart, int end) {
		int index = end;
		for (;;) {
			int offset = headerPart.indexOf('\r', index);
			if (offset == -1 || offset + 1 >= headerPart.length()) {
				throw new IllegalStateException("Expected headers to be terminated by an empty line.");
			}
			if (headerPart.charAt(offset + 1) == '\n') {
				return offset;
			}
			index = offset + 1;
		}
	}

	private String getFieldName(String contentDisposition) {
		String fieldName = null;

		if (contentDisposition != null && contentDisposition.toLowerCase().startsWith(FORM_DATA)) {
			ParameterParser parser = new ParameterParser();
			parser.setLowerCaseNames(true);

			// parameter parser can handle null input
			Map params = parser.parse(contentDisposition, ';');
			fieldName = (String) params.get("name");
			if (fieldName != null) {
				fieldName = fieldName.trim();
			}
		}

		return fieldName;
	}

	protected byte[] getBoundary(String contentType) {
		ParameterParser parser = new ParameterParser();
		parser.setLowerCaseNames(true);
		// Parameter parser can handle null input
		Map params = parser.parse(contentType, new char[] {';', ','});
		String boundaryStr = (String) params.get("boundary");

		if (boundaryStr == null) {
			return null;
		}

		byte[] boundary;
		try {
			boundary = boundaryStr.getBytes("ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			boundary = boundaryStr.getBytes();
		}
		return boundary;
	}

	private String getFileName(String contentDisposition) {
		String fileName = null;

		if (contentDisposition != null) {
			String cdl = contentDisposition.toLowerCase();

			if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) {

				ParameterParser parser = new ParameterParser();
				parser.setLowerCaseNames(true);

				// parameter parser can handle null input
				Map params = parser.parse(contentDisposition, ';');
				if (params.containsKey("filename")) {
					fileName = (String) params.get("filename");
					if (fileName != null) {
						fileName = fileName.trim();
					} else {
						// even if there is no value, the parameter is present,
						// so we return an empty file name rather than no file
						// name.
						fileName = "";
					}
				}
			}
		}

		return fileName;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy