源码(二) - FileUploadBase

一.概述

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是表单字段时为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;

	/**
	 * @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;
	}
}

四.内部类 - FileItemIteratorImpl

一个FileItem的迭代器,迭代器遍历可以解析出实体中的每个部分,创建对应的FileItem实例
private class FileItemIteratorImpl implements FileItemIterator {
	// 处理multi part stream
	private final MultipartStream multi;
	
	// 通知器,用于触发监听器
	private final MultipartStream.ProgressNotifier notifier;

	// 边界,分割各部分
	private final byte[] boundary;

	// 当前处理的FileItem
	private 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=----WebKitFormBoundarySvo5DUiFsRuugu9A
		String 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-8
		String 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-8
		multi.setHeaderEncoding(charEncoding);
		// 跳过序言
		skipPreamble = true;
		findNextItem();
	}

	/**
	 * 是否找得到下一个item
	 * @return true-表示下一个item被找到
	 */ 
	private boolean findNextItem() throws IOException {
		if (eof) {// 文件末尾
			return false;
		}
		if (currentItem != null) {// 关闭当前的item
			currentItem.close();
			currentItem = null;
		}
		for (;;) {
			// 是否还有下一个item
			boolean 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$983112430
				String 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 name
						byte[] subBoundary = getBoundary(subContentType);
						multi.setBoundary(subBoundary);
						skipPreamble = true;
						continue;
					}
					// 获取字段名对应的文件名,没有就为null
					String 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;
	}
}

四.完整类 - 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 stream
		private final MultipartStream multi;
		
		// 通知器,用于触发监听器
		private final MultipartStream.ProgressNotifier notifier;

		// 边界,分割各部分
		private final byte[] boundary;

		// 当前处理的FileItem
		private 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=----WebKitFormBoundarySvo5DUiFsRuugu9A
			String 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-8
			String 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-8
			multi.setHeaderEncoding(charEncoding);
			// 跳过序言
			skipPreamble = true;
			findNextItem();
		}

       	/**
		 * 是否找得到下一个item
		 * @return true-表示下一个item被找到
		 */ 
		private boolean findNextItem() throws IOException {
			if (eof) {// 文件末尾
				return false;
			}
			if (currentItem != null) {// 关闭当前的item
				currentItem.close();
				currentItem = null;
			}
			for (;;) {
				// 是否还有下一个item
				boolean 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$983112430
					String 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 name
							byte[] subBoundary = getBoundary(subContentType);
							multi.setBoundary(subBoundary);
							skipPreamble = true;
							continue;
						}
						// 获取字段名对应的文件名,没有就为null
						String 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;
    }
}













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值