源码(二)
来源:互联网 发布:asp开源cms 编辑:程序博客网 时间:2024/06/08 16:18
一.概述
1.位置 : org.apache.commons.fileupload.FileUploadBase.java
2.此类parseRequest(RequestContext ctx)方法用于解析上传文件,返回一个List<FileItem>
二.解析请求报文流程
三.内部类 - FileItemStreamImpl
3.1 接口FileItemHeadersSupport
package org.apache.commons.fileupload;public interface FileItemHeadersSupport { FileItemHeaders getHeaders(); void setHeaders(FileItemHeaders headers);}
3.2 接口
package org.apache.commons.fileupload;import java.io.IOException;import java.io.InputStream;public interface FileItemStream extends FileItemHeadersSupport { public static class ItemSkippedException extends IOException { private static final long serialVersionUID = -7280778431581963740L; } InputStream openStream() throws IOException; String getContentType(); String getName(); String getFieldName(); boolean isFormField();}
3.3 实现类FileItemStreamImpl
private class FileItemStreamImpl implements FileItemStream {// 当前FileItem的内容类型,FileItem是表单字段时为nullprivate final String contentType;// FileItem的字段名private final String fieldName;// 上传文件名,当FileItem是表单字段时为nullprivate final String name;// 当前FileItem是否是表单字段private final boolean formField;// 当前FileItem的输入流private final InputStream stream;// 当前FileItem是否已经打开private boolean opened;// 当前FileItem的headerprivate FileItemHeaders headers;/** * @param pName 文件名,或为null * @param pFieldName item的字段名,如text$983112430 * @param pContentType item的内容类型,或为null(是表单字段时) * @param pFormField 是否是表单字段,文件名为null就是表单字段 * @param pContentLength item的内容长度,或为-1(是表单字段时) */FileItemStreamImpl(String pName, String pFieldName, String pContentType, boolean pFormField, long pContentLength) throws IOException {name = pName;fieldName = pFieldName;contentType = pContentType;formField = pFormField;final ItemInputStream itemStream = multi.newInputStream();InputStream istream = itemStream;if (fileSizeMax != -1) {// 单个上传文件大小有限制if (pContentLength != -1 && pContentLength > fileSizeMax) {// 上传文件过大FileUploadException e = new FileSizeLimitExceededException("The field " + fieldName + " exceeds its maximum permitted " + " size of " + fileSizeMax + " characters.", pContentLength, fileSizeMax);throw new FileUploadIOException(e);}istream = new LimitedInputStream(istream, fileSizeMax) {protected void raiseError(long pSizeMax, long pCount) throws IOException {itemStream.close(true);FileUploadException e = new FileSizeLimitExceededException("The field " + fieldName + " exceeds its maximum permitted " + " size of " + pSizeMax + " characters.", pCount, pSizeMax);throw new FileUploadIOException(e);}};}stream = istream;}// 打开FileItem的文件流,用于读取FileItem的内容public InputStream openStream() throws IOException {if (opened) {throw new IllegalStateException("The stream was already opened.");}if (((Closeable) stream).isClosed()) {throw new FileItemStream.ItemSkippedException();}return stream;}// 关闭FileItem的流void close() throws IOException {stream.close();}public FileItemHeaders getHeaders() {return headers;}public void setHeaders(FileItemHeaders pHeaders) {headers = pHeaders;}// 返回当前FileItem的内容类型,当FileItem为表单字段时返回nullpublic String getContentType() {return contentType;}// 返回字段名public String getFieldName() {return fieldName;}// 返回上传文件名,当FileItem为表单字段时返回nullpublic String getName() {return name;}// 返回当前FileItem是否是表单字段public boolean isFormField() {return formField;}}
四.内部类 - FileItemIteratorImpl
一个FileItem的迭代器,迭代器遍历可以解析出实体中的每个部分,创建对应的FileItem实例
private class FileItemIteratorImpl implements FileItemIterator {// 处理multi part streamprivate final MultipartStream multi;// 通知器,用于触发监听器private final MultipartStream.ProgressNotifier notifier;// 边界,分割各部分private final byte[] boundary;// 当前处理的FileItemprivate FileItemStreamImpl currentItem;// 当前item字段名private String currentFieldName;// 是否跳过序言private boolean skipPreamble;// 当前FileItem是否有效,是否可以继续读取private boolean itemValid;// 是否是文件的结尾private boolean eof;// 构造函数FileItemIteratorImpl(RequestContext ctx) throws FileUploadException, IOException {if (ctx == null) {throw new NullPointerException("ctx parameter");}// 如:multipart/form-data; boundary=----WebKitFormBoundarySvo5DUiFsRuugu9AString contentType = ctx.getContentType();if ((null == contentType) || (!contentType.toLowerCase().startsWith(MULTIPART))) {throw new InvalidContentTypeException("the request doesn't contain a " + MULTIPART_FORM_DATA + " or " + MULTIPART_MIXED + " stream, content type header is " + contentType);}// 获取请求流InputStream input = ctx.getInputStream();if (sizeMax >= 0) {// 设置了整个上传文件的最大大小// 上传文件的长度int requestSize = ctx.getContentLength();if (requestSize == -1) {input = new LimitedInputStream(input, sizeMax) {protected void raiseError(long pSizeMax, long pCount) throws IOException {FileUploadException ex = new SizeLimitExceededException("the request was rejected because" + " its size (" + pCount + ") exceeds the configured maximum" + " (" + pSizeMax + ")", pCount, pSizeMax);throw new FileUploadIOException(ex);}};} else {if (sizeMax >= 0 && requestSize > sizeMax) {// 上传文件过大throw new SizeLimitExceededException("the request was rejected because its size (" + requestSize + ") exceeds the configured maximum (" + sizeMax + ")", requestSize, sizeMax);}}}// 如:UTF-8String charEncoding = headerEncoding;if (charEncoding == null) {charEncoding = ctx.getCharacterEncoding();}/** * contentType:multipart/form-data; boundary=----WebKitFormBoundarySvo5DUiFsRuugu9A * boundaryStr : ----WebKitFormBoundarySvo5DUiFsRuugu9A * byte[]:[45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 83, 118, 111, 53, 68, 85, 105, 70, 115, 82, 117, 117, 103, 117, 57, 65] */boundary = getBoundary(contentType);if (boundary == null) {// 没有找到边界throw new FileUploadException("the request was rejected because " + "no multipart boundary was found");}notifier = new MultipartStream.ProgressNotifier(listener, ctx.getContentLength());// 处理多部分流multi = new MultipartStream(input, boundary, notifier);// UTF-8multi.setHeaderEncoding(charEncoding);// 跳过序言skipPreamble = true;findNextItem();}/** * 是否找得到下一个item * @return true-表示下一个item被找到 */ private boolean findNextItem() throws IOException {if (eof) {// 文件末尾return false;}if (currentItem != null) {// 关闭当前的itemcurrentItem.close();currentItem = null;}for (;;) {// 是否还有下一个itemboolean nextPart;if (skipPreamble) {// 跳过序言nextPart = multi.skipPreamble();} else {nextPart = multi.readBoundary();}if (!nextPart) {// if (currentFieldName == null) {// 没有字段名// 外部multipart终止 - >没有更多的数据eof = true;return false;}// 内部multipart已终止 - >返回解析外部multi.setBoundary(boundary);currentFieldName = null;continue;}/** * 解析当前encapsulation的header-part * 如header:Content-Disposition: form-data; name="text$6822755630" */FileItemHeaders headers = getParsedHeaders(multi.readHeaders());if (currentFieldName == null) {// 当前字段名为null// 解析出的字段名,如text$983112430String fieldName = getFieldName(headers);if (fieldName != null) {// 有字段名String subContentType = headers.getHeader(CONTENT_TYPE);if (subContentType != null && subContentType.toLowerCase().startsWith(MULTIPART_MIXED)) {currentFieldName = fieldName;// Multiple files associated with this field namebyte[] subBoundary = getBoundary(subContentType);multi.setBoundary(subBoundary);skipPreamble = true;continue;}// 获取字段名对应的文件名,没有就为nullString fileName = getFileName(headers);currentItem = new FileItemStreamImpl(fileName, fieldName, headers.getHeader(CONTENT_TYPE), fileName == null, getContentLength(headers));notifier.noteItem();// item是有效的itemValid = true;return true;}} else {// 有上传文件String fileName = getFileName(headers);if (fileName != null) {currentItem = new FileItemStreamImpl(fileName, currentFieldName, headers.getHeader(CONTENT_TYPE), false, getContentLength(headers));notifier.noteItem();itemValid = true;return true;}}multi.discardBodyData();}}// 从pHeaders获取内容长度private long getContentLength(FileItemHeaders pHeaders) {try {return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH));//"Content-length"} catch (Exception e) {return -1;}}// 是否还有下一个FileItempublic boolean hasNext() throws FileUploadException, IOException {if (eof) {// 文件末尾,表示文件读完return false;}if (itemValid) {// 当前的item是可以读取的return true;}return findNextItem();}/** * 返回下一个可用的FileItemStream实例 */public FileItemStream next() throws FileUploadException, IOException {if (eof || (!itemValid && !hasNext())) {throw new NoSuchElementException();}itemValid = false;return currentItem;}}
四.完整类 - FileUploadBase
package org.apache.commons.fileupload;import java.io.IOException;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.NoSuchElementException;import javax.servlet.http.HttpServletRequest;import org.apache.commons.fileupload.MultipartStream.ItemInputStream;import org.apache.commons.fileupload.servlet.ServletFileUpload;import org.apache.commons.fileupload.servlet.ServletRequestContext;import org.apache.commons.fileupload.util.Closeable;import org.apache.commons.fileupload.util.FileItemHeadersImpl;import org.apache.commons.fileupload.util.LimitedInputStream;import org.apache.commons.fileupload.util.Streams;/** * <p>用于处理文件上传的高级API</p> * * 本类处理由html组件发送的multipart/mixed编码类型、RFC 1867(http://www.ietf.org/rfc/rfc1867.txt)指定的多个文件 * 使用parseRequest(HttpServletRequest)方法获取一个org.apache.commons.fileupload.FileItem列表 * * 上传文件是保存在内存,缓存到本地,还是其他地方,由DiskFileItemFactory决定 * 已弃用的代码删除了 */ public abstract class FileUploadBase { /** * 确定请求是否包含多部分内容。可用来判断是否是文件上传请求 * @param request servlet请求对象,必须非空 * @return true:文件上传请求 */ public static boolean isMultipartContent(HttpServletRequest req) { return ServletFileUpload.isMultipartContent(req); }// 不再使用此方法,使用ServletFileUpload中对于方法替代 public static final boolean isMultipartContent(RequestContext ctx) { String contentType = ctx.getContentType(); if (contentType == null) { return false; } if (contentType.toLowerCase().startsWith(MULTIPART)) { return true; } return false; }// 如:Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet public static final String CONTENT_TYPE = "Content-type"; // 如:Content-Disposition: form-data; name="fileName"; filename="123.xlsx" public static final String CONTENT_DISPOSITION = "Content-disposition"; public static final String CONTENT_LENGTH = "Content-length"; /** * Content-disposition值中的form-data * 如:Content-Disposition: form-data; name="fileName"; filename="123.xlsx" */ public static final String FORM_DATA = "form-data"; // Content-disposition值中的attachment public static final String ATTACHMENT = "attachment"; public static final String MULTIPART = "multipart/"; public static final String MULTIPART_FORM_DATA = "multipart/form-data"; public static final String MULTIPART_MIXED = "multipart/mixed";// 不再使用 public static final int MAX_HEADER_SIZE = 1024;// 完整请求的最大值,-1表示没有最大值 private long sizeMax = -1; // 单个上传文件的最大值,-1表示没有最大值 private long fileSizeMax = -1;// 读取header时的内容编码 private String headerEncoding; // 进度监听器 private ProgressListener listener;// 返回用于创建FileItem的FileItemFactory public abstract FileItemFactory getFileItemFactory(); public abstract void setFileItemFactory(FileItemFactory factory); public FileItemIterator getItemIterator(RequestContext ctx) throws FileUploadException, IOException { return new FileItemIteratorImpl(ctx); }/** * 解析请求流,返回一个FileItem List */ public List /* FileItem */ parseRequest(RequestContext ctx) throws FileUploadException { try { FileItemIterator iter = getItemIterator(ctx); List items = new ArrayList();// 获取创建FileItem的工厂FileItemFactory FileItemFactory fac = getFileItemFactory(); if (fac == null) { throw new NullPointerException("No FileItemFactory has been set."); } while (iter.hasNext()) {// 返回一个item FileItemStream item = iter.next();// 根据item创建对于的FileItem FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(), item.isFormField(), item.getName()); try {// Streams.copy(item.openStream(), fileItem.getOutputStream(), true); } catch (FileUploadIOException e) { throw (FileUploadException) e.getCause(); } catch (IOException e) { throw new IOFileUploadException("Processing of " + MULTIPART_FORM_DATA + " request failed. " + e.getMessage(), e); } if (fileItem instanceof FileItemHeadersSupport) { final FileItemHeaders fih = item.getHeaders(); ((FileItemHeadersSupport) fileItem).setHeaders(fih); }// 将创建的FileItem添加到List<ListItem>中 items.add(fileItem); } return items; } catch (FileUploadIOException e) { throw (FileUploadException) e.getCause(); } catch (IOException e) { throw new FileUploadException(e.getMessage(), e); } } /** * 从contentType中检索出边界boundary * 如: * Content-Type = multipart/form-data; boundary=----WebKitFormBoundarypv0XnanNgIkhNEDD * 返回的boundary:----WebKitFormBoundarypv0XnanNgIkhNEDD */ 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; } /** * 从headers的key=Content-disposition对应值中返回文件名 * @param headers FileItem的header, {content-disposition=[form-data; name="text$5234303740"]} * @return 返回当前encapsulation的文件名 */ protected String getFileName(FileItemHeaders headers) {// headers.getHeader(CONTENT_DISPOSITION) - form-data; name="text$5234303740" return getFileName(headers.getHeader(CONTENT_DISPOSITION));// "Content-disposition" }/** * 从pContentDisposition中获取File文件名 * @param pContentDisposition 如:form-data; name="text$5234303740" * @return 返回text$5234303740 */ private String getFileName(String pContentDisposition) { String fileName = null; if (pContentDisposition != null) {// 存在文件名 String cdl = pContentDisposition.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(pContentDisposition, ';'); if (params.containsKey("filename")) { fileName = (String) params.get("filename"); if (fileName != null) { fileName = fileName.trim(); } else { // 即使没有值,参数也是存在的,所以我们返回一个空的文件名而不是文件名。 fileName = ""; } } } } return fileName; } /** * 从FileItemHeaders的key=Content-disposition的值中检索字段名 * @param headers 包含HTTP请求头的一个Map,有一个键值对,key="Content-disposition" * @return 返回当前encapsulation的字段名 */ protected String getFieldName(FileItemHeaders headers) {// CONTENT_DISPOSITION = "Content-disposition" return getFieldName(headers.getHeader(CONTENT_DISPOSITION)); } /** * 根据所给的content-disposition值返回字段名 * @param pContentDisposition content-dispositions值,如-form-data; name="text$983112430" * @return 返回字段,如text$983112430 */ private String getFieldName(String pContentDisposition) { String fieldName = null;// FORM_DATA = "form-data" if (pContentDisposition != null && pContentDisposition.toLowerCase().startsWith(FORM_DATA)) { ParameterParser parser = new ParameterParser(); parser.setLowerCaseNames(true); // 参数解析器可以处理空输入 Map params = parser.parse(pContentDisposition, ';');// 字段名 fieldName = (String) params.get("name"); if (fieldName != null) { fieldName = fieldName.trim(); } } return fieldName; } /** * 解析header-part,并返回键值对 * 如: * Content-Disposition: form-data; name="text$5234303740" * * @param headerPart 当前encapsulation的header-part * @return 返回一个包含解析出的HTTP请求头的Map */ protected FileItemHeaders getParsedHeaders(String headerPart) { final int len = headerPart.length(); FileItemHeadersImpl headers = newFileItemHeaders(); int start = 0; for (;;) {// headerPart的/r/n的索引,此例返回54 int end = parseEndOfLine(headerPart, start); if (start == end) { break; }// 当前header:此例-Content-Disposition: form-data; name="text$983112430" 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; }// 解析当前header,记录在headers中,此例header=Content-Disposition: form-data; name="text$983112430" parseHeaderLine(headers, header); } return headers; } /** * 解析header中的一行,将解析的结果保存到headers中 * 如:header=Content-Disposition: form-data; name="text$5234303740" * 结果:content-disposition=[form-data; name="text$5234303740"] * @param headers 记录解析header的键值对 * @param header 待解析的header行 */ private void parseHeaderLine(FileItemHeadersImpl headers, String header) { final int colonOffset = header.indexOf(':'); if (colonOffset == -1) { // 这个标题行格式错误,跳过它。 return; } String headerName = header.substring(0, colonOffset).trim(); String headerValue = header.substring(header.indexOf(':') + 1).trim(); headers.addHeader(headerName, headerValue); } /** * 从end处开始查找\r\n的位置,即返回行字符串的结尾索引 * 如:headerPart=Content-Disposition: form-data; name="text$5234303740",结尾返回54 * @param headerPart 正在被解析headers * @param end 从索引end处开始搜索 * @return \r\n序列的索引,表示行尾。 */ 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."); }// /r/n 说明找到 if (headerPart.charAt(offset + 1) == '\n') { return offset; } index = offset + 1; } } protected FileItemHeadersImpl newFileItemHeaders() { return new FileItemHeadersImpl(); } /** * 由FileUploadBase#getItemIterator(RequestContext)调用获取此迭代器 */ private class FileItemIteratorImpl implements FileItemIterator { /** * Default implementation of {@link FileItemStream}. */ private class FileItemStreamImpl implements FileItemStream { // 当前FileItem的内容类型,FileItem是表单字段时为null private final String contentType; // FileItem的字段名 private final String fieldName; // 上传文件名,当FileItem是表单字段时为null private final String name; // 当前FileItem是否是表单字段 private final boolean formField; // 当前FileItem的输入流 private final InputStream stream; // 当前FileItem是否已经打开 private boolean opened; // 当前FileItem的header private FileItemHeaders headers; /** * Creates a new instance. * @param pName 文件名,或为null * @param pFieldName item的字段名,如text$983112430 * @param pContentType item的内容类型,或为null(是表单字段时) * @param pFormField 是否是表单字段,文件名为null就是表单字段 * @param pContentLength item的内容长度,或为-1(是表单字段时) */ FileItemStreamImpl(String pName, String pFieldName, String pContentType, boolean pFormField, long pContentLength) throws IOException { name = pName; fieldName = pFieldName; contentType = pContentType; formField = pFormField; final ItemInputStream itemStream = multi.newInputStream(); InputStream istream = itemStream; if (fileSizeMax != -1) {// 单个上传文件大小有限制 if (pContentLength != -1 && pContentLength > fileSizeMax) {// 上传文件过大 FileUploadException e = new FileSizeLimitExceededException("The field " + fieldName + " exceeds its maximum permitted " + " size of " + fileSizeMax + " characters.", pContentLength, fileSizeMax); throw new FileUploadIOException(e); } istream = new LimitedInputStream(istream, fileSizeMax) { protected void raiseError(long pSizeMax, long pCount) throws IOException { itemStream.close(true); FileUploadException e = new FileSizeLimitExceededException("The field " + fieldName + " exceeds its maximum permitted " + " size of " + pSizeMax + " characters.", pCount, pSizeMax); throw new FileUploadIOException(e); } }; } stream = istream; }// 打开FileItem的文件流,用于读取FileItem的内容 public InputStream openStream() throws IOException { if (opened) { throw new IllegalStateException("The stream was already opened."); } if (((Closeable) stream).isClosed()) { throw new FileItemStream.ItemSkippedException(); } return stream; } // 关闭FileItem的流 void close() throws IOException { stream.close(); } public FileItemHeaders getHeaders() { return headers; } public void setHeaders(FileItemHeaders pHeaders) { headers = pHeaders; } // 返回当前FileItem的内容类型,当FileItem为表单字段时返回null public String getContentType() { return contentType; } // 返回字段名 public String getFieldName() { return fieldName; } // 返回上传文件名,当FileItem为表单字段时返回null public String getName() { return name; } // 返回当前FileItem是否是表单字段 public boolean isFormField() { return formField; } } // 处理multi part streamprivate final MultipartStream multi;// 通知器,用于触发监听器private final MultipartStream.ProgressNotifier notifier;// 边界,分割各部分private final byte[] boundary;// 当前处理的FileItemprivate FileItemStreamImpl currentItem;// 当前item字段名private String currentFieldName;// 是否跳过序言private boolean skipPreamble;// 当前FileItem是否有效,是否可以继续读取private boolean itemValid;// 是否是文件的结尾private boolean eof;// 构造函数FileItemIteratorImpl(RequestContext ctx) throws FileUploadException, IOException {if (ctx == null) {throw new NullPointerException("ctx parameter");}// 如:multipart/form-data; boundary=----WebKitFormBoundarySvo5DUiFsRuugu9AString contentType = ctx.getContentType();if ((null == contentType) || (!contentType.toLowerCase().startsWith(MULTIPART))) {throw new InvalidContentTypeException("the request doesn't contain a " + MULTIPART_FORM_DATA + " or " + MULTIPART_MIXED + " stream, content type header is " + contentType);}// 获取请求流InputStream input = ctx.getInputStream();if (sizeMax >= 0) {// 设置了整个上传文件的最大大小// 上传文件的长度int requestSize = ctx.getContentLength();if (requestSize == -1) {input = new LimitedInputStream(input, sizeMax) {protected void raiseError(long pSizeMax, long pCount) throws IOException {FileUploadException ex = new SizeLimitExceededException("the request was rejected because" + " its size (" + pCount + ") exceeds the configured maximum" + " (" + pSizeMax + ")", pCount, pSizeMax);throw new FileUploadIOException(ex);}};} else {if (sizeMax >= 0 && requestSize > sizeMax) {// 上传文件过大throw new SizeLimitExceededException("the request was rejected because its size (" + requestSize + ") exceeds the configured maximum (" + sizeMax + ")", requestSize, sizeMax);}}}// 如:UTF-8String charEncoding = headerEncoding;if (charEncoding == null) {charEncoding = ctx.getCharacterEncoding();}/** * contentType:multipart/form-data; boundary=----WebKitFormBoundarySvo5DUiFsRuugu9A * boundaryStr : ----WebKitFormBoundarySvo5DUiFsRuugu9A * byte[]:[45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 83, 118, 111, 53, 68, 85, 105, 70, 115, 82, 117, 117, 103, 117, 57, 65] */boundary = getBoundary(contentType);if (boundary == null) {// 没有找到边界throw new FileUploadException("the request was rejected because " + "no multipart boundary was found");}notifier = new MultipartStream.ProgressNotifier(listener, ctx.getContentLength());// 处理多部分流multi = new MultipartStream(input, boundary, notifier);// UTF-8multi.setHeaderEncoding(charEncoding);// 跳过序言skipPreamble = true;findNextItem();} /** * 是否找得到下一个item * @return true-表示下一个item被找到 */ private boolean findNextItem() throws IOException {if (eof) {// 文件末尾return false;}if (currentItem != null) {// 关闭当前的itemcurrentItem.close();currentItem = null;}for (;;) {// 是否还有下一个itemboolean nextPart;if (skipPreamble) {// 跳过序言nextPart = multi.skipPreamble();} else {nextPart = multi.readBoundary();}if (!nextPart) {// if (currentFieldName == null) {// 没有字段名// 外部multipart终止 - >没有更多的数据eof = true;return false;}// 内部multipart已终止 - >返回解析外部multi.setBoundary(boundary);currentFieldName = null;continue;}/** * 解析当前encapsulation的header-part * 如header:Content-Disposition: form-data; name="text$6822755630" */FileItemHeaders headers = getParsedHeaders(multi.readHeaders());if (currentFieldName == null) {// 当前字段名为null// 解析出的字段名,如text$983112430String fieldName = getFieldName(headers);if (fieldName != null) {// 有字段名String subContentType = headers.getHeader(CONTENT_TYPE);if (subContentType != null && subContentType.toLowerCase().startsWith(MULTIPART_MIXED)) {currentFieldName = fieldName;// Multiple files associated with this field namebyte[] subBoundary = getBoundary(subContentType);multi.setBoundary(subBoundary);skipPreamble = true;continue;}// 获取字段名对应的文件名,没有就为nullString fileName = getFileName(headers);currentItem = new FileItemStreamImpl(fileName, fieldName, headers.getHeader(CONTENT_TYPE), fileName == null, getContentLength(headers));notifier.noteItem();// item是有效的itemValid = true;return true;}} else {// 有上传文件String fileName = getFileName(headers);if (fileName != null) {currentItem = new FileItemStreamImpl(fileName, currentFieldName, headers.getHeader(CONTENT_TYPE), false, getContentLength(headers));notifier.noteItem();itemValid = true;return true;}}multi.discardBodyData();}}// 从pHeaders获取内容长度 private long getContentLength(FileItemHeaders pHeaders) { try { return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH));//"Content-length" } catch (Exception e) { return -1; } }// 是否还有下一个FileItem public boolean hasNext() throws FileUploadException, IOException { if (eof) {// 文件末尾,表示文件读完 return false; } if (itemValid) {// 当前的item是可以读取的 return true; } return findNextItem(); } /** * 返回下一个可用的FileItemStream实例 */ public FileItemStream next() throws FileUploadException, IOException { if (eof || (!itemValid && !hasNext())) { throw new NoSuchElementException(); } itemValid = false; return currentItem; } } /** * This exception is thrown for hiding an inner * {@link FileUploadException} in an {@link IOException}. */ public static class FileUploadIOException extends IOException { // The exceptions UID, for serializing an instance. private static final long serialVersionUID = -7047616958165584154L; // The exceptions cause; we overwrite the parent classes field, which is available since Java 1.4 only. private final FileUploadException cause; /** * Creates a <code>FileUploadIOException</code> with the given cause. * @param pCause The exceptions cause, if any, or null. */ public FileUploadIOException(FileUploadException pCause) { // We're not doing super(pCause) cause of 1.3 compatibility. cause = pCause; } /** * Returns the exceptions cause. * @return The exceptions cause, if any, or null. */ public Throwable getCause() { return cause; } } /** * Thrown to indicate that the request is not a multipart request. */ public static class InvalidContentTypeException extends FileUploadException { private static final long serialVersionUID = -9073026332015646668L; /** * Constructs a <code>InvalidContentTypeException</code> with no detail message. */ public InvalidContentTypeException() { // Nothing to do. } /** * Constructs an <code>InvalidContentTypeException</code> with the specified detail message. * @param message The detail message. */ public InvalidContentTypeException(String message) { super(message); } } /** * Thrown to indicate an IOException. */ public static class IOFileUploadException extends FileUploadException { private static final long serialVersionUID = 1749796615868477269L; // The exceptions cause; we overwrite the parent classes field, which is available since Java 1.4 only. private final IOException cause; /** * Creates a new instance with the given cause. * @param pMsg The detail message. * @param pException The exceptions cause. */ public IOFileUploadException(String pMsg, IOException pException) { super(pMsg); cause = pException; } /** * Returns the exceptions cause. * @return The exceptions cause, if any, or null. */ public Throwable getCause() { return cause; } } /** This exception is thrown, if a requests permitted size * is exceeded. */ protected abstract static class SizeException extends FileUploadException { /** * The actual size of the request. */ private final long actual; /** * The maximum permitted size of the request. */ private final long permitted; /** * Creates a new instance. * @param message The detail message. * @param actual The actual number of bytes in the request. * @param permitted The requests size limit, in bytes. */ protected SizeException(String message, long actual, long permitted) { super(message); this.actual = actual; this.permitted = permitted; } /** * Retrieves the actual size of the request. * * @return The actual size of the request. */ public long getActualSize() { return actual; } /** * Retrieves the permitted size of the request. * * @return The permitted size of the request. */ public long getPermittedSize() { return permitted; } } /** * Thrown to indicate that the request size is not specified. In other * words, it is thrown, if the content-length header is missing or * contains the value -1. * @deprecated As of commons-fileupload 1.2, the presence of a * content-length header is no longer required. */ public static class UnknownSizeException extends FileUploadException { /** The exceptions UID, for serializing an instance. */ private static final long serialVersionUID = 7062279004812015273L; /** * Constructs a <code>UnknownSizeException</code> with no * detail message. */ public UnknownSizeException() { super(); } /** * Constructs an <code>UnknownSizeException</code> with * the specified detail message. * * @param message The detail message. */ public UnknownSizeException(String message) { super(message); } } /** * Thrown to indicate that the request size exceeds the configured maximum. */ public static class SizeLimitExceededException extends SizeException { /** The exceptions UID, for serializing an instance. */ private static final long serialVersionUID = -2474893167098052828L; /** * @deprecated Replaced by * {@link #SizeLimitExceededException(String, long, long)} */ public SizeLimitExceededException() { this(null, 0, 0); } /** * @deprecated Replaced by * {@link #SizeLimitExceededException(String, long, long)} * @param message The exceptions detail message. */ public SizeLimitExceededException(String message) { this(message, 0, 0); } /** * Constructs a <code>SizeExceededException</code> with * the specified detail message, and actual and permitted sizes. * * @param message The detail message. * @param actual The actual request size. * @param permitted The maximum permitted request size. */ public SizeLimitExceededException(String message, long actual, long permitted) { super(message, actual, permitted); } } /** * Thrown to indicate that A files size exceeds the configured maximum. */ public static class FileSizeLimitExceededException extends SizeException { /** The exceptions UID, for serializing an instance. */ private static final long serialVersionUID = 8150776562029630058L; /** * Constructs a <code>SizeExceededException</code> with * the specified detail message, and actual and permitted sizes. * * @param message The detail message. * @param actual The actual request size. * @param permitted The maximum permitted request size. */ public FileSizeLimitExceededException(String message, long actual, long permitted) { super(message, actual, permitted); } }// 返回完整请求的最大值,单位字节, -1表示没有限制 public long getSizeMax() { return sizeMax; } public void setSizeMax(long sizeMax) { this.sizeMax = sizeMax; } public long getFileSizeMax() { return fileSizeMax; } public void setFileSizeMax(long fileSizeMax) { this.fileSizeMax = fileSizeMax; } /** * 返回读取单个部分header时使用的字符编码 * 如果未指定或为null,就使用请求的编码 * 如果请求编码也未指定或为null,就使用平台的默认编码 */ public String getHeaderEncoding() { return headerEncoding; } public void setHeaderEncoding(String encoding) { headerEncoding = encoding; } public ProgressListener getProgressListener() { return listener; } public void setProgressListener(ProgressListener pListener) { listener = pListener; }}
阅读全文
0 0
- 源码分享(二)
- OOC源码(二)
- 源码阅读(二)
- 源码(二)
- android事件分发(二)源码源码
- 源码有毒:Jfinal源码解析(二)
- JUnit源码分析(二)
- Tomat源码学习(二)
- Tomat源码学习(二)
- Log4net源码分析(二)
- Tomat源码学习(二)
- Tomat源码学习(二)
- udhcp源码详解(二)
- Log4net源码分析(二)
- struts2源码浅析(二)
- Logcat源码分析(二)
- Tomat源码学习(二)
- FFMPEG源码分析(二)
- HDU
- java可视化编程-eclipse安装windowbuilder插件
- 迷途指针
- leetcode 657. Judge Route Circle
- oracle学习笔记
- 源码(二)
- 关于js原型和继承的方法复习
- WinRAR x64 v5.5中文版去广告过程
- 1003 Dijkstra
- 二叉搜索树的后序遍历序列
- Oracle DDL语句及示例代码
- MD5算法原理
- HDU 2296 Ring(AC自动机+DP)
- git 基础