springmvc处理文件上传

所有请求到后台的请求都会被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处理文件上传的逻辑就是这样

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值