源码(二)

来源:互联网 发布: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;    }}