所有请求到后台的请求都会被DispatcherServlet拦截到,然后由DispatcherServlet统一处理
protected void doService(HttpServletRequest request, HttpServletResponse response){ //此方法做真正的转发处理 doDispatch(request, response); }
protected void doDispatch(HttpServletRequest request, HttpServletResponse response){ HttpServletRequest processedRequest = request; //首先会判断当前的request是否是multipart类型的请求,也就是判断请求头中是否含有multipart字段 processedRequest = checkMultipart(request); }
/** 在spring中,可以指定multipartResolver为Commons包中的CommonsMultipartResolver类的实例,如果不指定,那么默认的是 StandardServletMultipartResolver类的实例 判断是否是multpart类型的rquest,本质上就是判断ContentType中是否包含multipart字段 public boolean isMultipart(HttpServletRequest request) { return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/"); } **/ protected HttpServletRequest checkMultipart(HttpServletRequest request){ //判断是否是multipart类型的request if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)){ //如果request就是MultipartHttpServletRequest类的实例,那么不用做任何出,否则需要创建一个MultipartHttpServletRequest实例 if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null){ 。。。。 }else{ //从下面一段代码可以知道,此方法返会的StandardMultipartHttpServletRequest实例 return this.multipartResolver.resolveMultipart(request); } } return request; }
@Override public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException { return new StandardMultipartHttpServletRequest(request, this.resolveLazily); }
创建StandardMultipartHttpServletRequest对象的时候,会做很多事情,解析请求,就是在初始化的时候解析的。
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing) throws MultipartException { super(request); if (!lazyParsing) { parseRequest(request); } }
解析请求
private void parseRequest(HttpServletRequest request) { try { //request.getParts()这个方法是重点,它把请求中的字节数组变成了文件。request对象是Tomcat中Request类的实例 Collection<Part> parts = request.getParts(); this.multipartParameterNames = new LinkedHashSet<>(parts.size()); MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size()); for (Part part : parts) { String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION); ContentDisposition disposition = ContentDisposition.parse(headerValue); String filename = disposition.getFilename(); if (filename != null) { if (filename.startsWith("=?") && filename.endsWith("?=")) { filename = MimeDelegate.decode(filename); } files.add(part.getName(), new StandardMultipartFile(part, filename)); } else { this.multipartParameterNames.add(part.getName()); } } setMultipartFiles(files); } catch (Throwable ex) { handleParseFailure(ex); } }
public Collection<Part> getParts(){ //下面一段代码就是把字节数组转换成文件的逻辑 this.parseParts(true); return this.parts; }
private void parseParts(boolean explicit){ //生成临时文件,储存文件 File location = (File)context.getServletContext().getAttribute("javax.servlet.context.tempdir"); DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setRepository(location.getCanonicalFile()); factory.setSizeThreshold(mce.getFileSizeThreshold()); ServletFileUpload upload = new ServletFileUpload(); upload.setFileItemFactory(factory); upload.setFileSizeMax(mce.getMaxFileSize()); upload.setSizeMax(mce.getMaxRequestSize()); //解析请求,并生成List<FileItem>集合 具体解析在1-1中 List<FileItem> items = upload.parseRequest(new ServletRequestContext(this)); }
/* 1-1 */ public List<FileItem> parseRequest(RequestContext ctx){ List<FileItem> items = new ArrayList(); FileItem fileItem; ArrayList var30; //创建文件迭代器,iter是FileItemIteratorImpl类的实例,初始化FileItemIteratorImpl类的时候,会从request中找到第一个文件 FileItemIterator iter = this.getItemIterator(ctx); //获取文件项工厂,工厂是DiskFileItemFactory类的实例 FileItemFactory fac = this.getFileItemFactory(); byte[] buffer = new byte[8192]; while(true){ /* 初始化FileItemIteratorImpl类的时候,会默认调用findNextItem()方法,给this.currentItem,itemValid赋值。当调用hasNext()方法时 首先会判断是否到已经读到流的结尾,如果已经读取完毕,返回false,如果没有,则判断itemValid是否true,如果为true表示已经读取过,则直接 返回true,最后会重新调用findNextItem()方法读取下一部分 */ if (!iter.hasNext()){ break; } //item是由FileItemIterator实例创建出来的FileItemStreamImpl对象 FileItemStream item = iter.next(); //文件项工厂创建文件项 fileItem = fac.createItem(item.getFieldName(), item.getContentType(), item.isFormField(), fileName); //每解析出一个文件流,都输出到对应的临时文件中 //item.openStream()返回的是一个输入流,这个输入流就是MultipartStream的实例内部类ItemInputStream的实例 //Streams.copy方法分析在1-3 Streams.copy(item.openStream(), fileItem.getOutputStream(), true, buffer); } }
public FileItemIteratorImpl(FileUploadBase pFileUploadBase, RequestContext pRequestContext) throws FileUploadException, IOException { this.fileUploadBase = pFileUploadBase; this.sizeMax = this.fileUploadBase.getSizeMax(); this.fileSizeMax = this.fileUploadBase.getFileSizeMax(); this.ctx = pRequestContext; if (this.ctx == null) { throw new NullPointerException("ctx parameter"); } else { this.skipPreamble = true; //从请求流中解析第一个文件 1-2 this.findNextItem(); } }
/*1-2*/ private boolean findNextItem(){ if (this.eof) { return false; } //调用getMultiPartStream这个方法时,如果this.multiPartStream为空,则会调用this.init方法进行初始化 //init方法内部 this.multiPartStream = new MultipartStream((InputStream)input, this.multiPartBoundary, this.progressNotifier); //给multiPartStream属性赋值 MultipartStream multi = this.getMultiPartStream(); while(true){ boolean nextPart; if (this.skipPreamble){ nextPart = multi.skipPreamble(); }else{ nextPart = multi.readBoundary(); } if (nextPart){ this.currentItem = new FileItemStreamImpl(this, fieldName, this.currentFieldName, headers.getHeader("Content-type"), false, this.getContentLength(headers)); } } }
FileItemStreamImpl类的构造函数如下:
public FileItemStreamImpl(FileItemIteratorImpl pFileItemIterator, String pName, String pFieldName, String pContentType, boolean pFormField, long pContentLength){ this.fileItemIteratorImpl = pFileItemIterator; this.name = pName; this.fieldName = pFieldName; this.contentType = pContentType; this.formField = pFormField; //调用MultipartStream实例的newInputStream方法 //new MultipartStream.ItemInputStream(),ItemInputStream为MultipartStream的实例内部类,所以ItemInputStream能访问MultipartStream实例的 //所有属性和方法 final ItemInputStream itemStream = this.fileItemIteratorImpl.getMultiPartStream().newInputStream(); InputStream istream = itemStream; this.stream = (InputStream)istream; }
public InputStream openStream() throws IOException { if (((Closeable)this.stream).isClosed()) { throw new ItemSkippedException(); } else { return this.stream; } }
/* 1-3 */ public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream, byte[] buffer){ OutputStream out = outputStream; InputStream in = inputStream; long total = 0L; while(true){ //in 是MultipartStream的实例内部类ItemInputStream的实例 int res = in.read(buffer); out.write(buffer, 0, res); } }
ItemInputStream的read方法
public int read(byte[] b, int off, int len){ int res = this.available(); if (res == 0){ res = this.makeAvailable(); if (res == 0){ return -1; } res = Math.min(res, len); //把MultipartStream中的缓存拷贝到byte[] b中 System.arraycopy(MultipartStream.this.buffer, MultipartStream.this.head, b, off, res); MultipartStream.this.head = MultipartStream.this.head + res; this.total += (long)res; return res; } }
springmvc处理文件上传的逻辑就是这样