深入springMVC源码------文件上传源码解析(下篇)

在上篇《深入springMVC------文件上传源码解析(上篇) 》中,介绍了springmvc文件上传相关。那么本篇呢,将进一步介绍springmvc 上传文件的效率问题。

相信大部分人在处理文件上传逻辑的时候会直接获取输入流直接进行操作,伪代码类似这样:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public ResultView upload(@RequestParam("file") MultipartFile file) {
    Inputstream in = file.getInputStream();
    ...
}

但是,出于效率,其实我个人更推荐使用 MultipartFile 的 transferTo 方法进行操作,类似这样:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public ResultView upload(@RequestParam("file") MultipartFile file) {
    file.transferTo(new File(destFile));
    ...
}

为什么呢?这个就得从源码说起,废话不多说,咱们直接去看源码吧:

1. 先看 MultipartFile(其实现类CommonsMultipartFile) 的getInputStream方法:

CommonsMultipartFile:

public InputStream getInputStream() throws IOException {
        if (!isAvailable()) {
            throw new IllegalStateException("File has been moved - cannot be read again");
        }
        InputStream inputStream = this.fileItem.getInputStream();
        return (inputStream != null ? inputStream : new ByteArrayInputStream(new byte[0]));
    }

通过源码可以看到,spring是通过commons-fileupload 中的FileItem对象去获取输入流,那么就去看看FileItem(其实现类DiskFileItem)的对应方法:

DiskFileItem:

public InputStream getInputStream()
        throws IOException {
        if (!isInMemory()) {
            return new FileInputStream(dfos.getFile());
        }

        if (cachedContent == null) {
            cachedContent = dfos.getData();
        }
        return new ByteArrayInputStream(cachedContent);
    }

通过源码可以看到:先去查看是否存在于内存中,如果存在,就将内存中的file对象包装为文件流, 如果不存在,那么就去看缓存,如果缓存存在就从缓存中获取字节数组并包装为输入流。

接下来,咱们再看看 CommonsMultipartFile 的 transferTo 方法,以便形成比较:

CommonsMultipartFile:

@Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        if (!isAvailable()) {
            throw new IllegalStateException("File has already been moved - cannot be transferred again");
        }

        if (dest.exists() && !dest.delete()) {
            throw new IOException(
                    "Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
        }

        try {
            this.fileItem.write(dest);
            if (logger.isDebugEnabled()) {
                String action = "transferred";
                if (!this.fileItem.isInMemory()) {
                    action = isAvailable() ? "copied" : "moved";
                }
                logger.debug("Multipart file ‘" + getName() + "‘ with original filename [" +
                        getOriginalFilename() + "], stored " + getStorageDescription() + ": " +
                        action + " to [" + dest.getAbsolutePath() + "]");
            }
        }
        catch (FileUploadException ex) {
            throw new IllegalStateException(ex.getMessage());
        }
        catch (IOException ex) {
            throw ex;
        }
        catch (Exception ex) {
            logger.error("Could not transfer to file", ex);
            throw new IOException("Could not transfer to file: " + ex.getMessage());
        }
    }

不多说,主要看 this.fileItem.write(dest) 这一句,利用commons-fileupload 中的相关方法:

DiskFileItem:

public void write(File file) throws Exception {
        if (isInMemory()) {
            FileOutputStream fout = null;
            try {
                fout = new FileOutputStream(file);
                fout.write(get());
            } finally {
                if (fout != null) {
                    fout.close();
                }
            }
        } else {
            File outputFile = getStoreLocation();
            if (outputFile != null) {
                // Save the length of the file
                size = outputFile.length();
........

通过源码可以看到 transfoTo 方法很干净利落,直接去将内存中的文件通过输出流写出到指定的file 。 等等,跟上面的 getInputStream方法相比,是不是省了点步骤? 是的,再来一张图,清晰地表示两个方法地不同之处:

图中:

红色线表示使用的是transferTo方法,黑色线代表getInputStream方法, 可见,transferTo直接将内存中的文件缓存直接写入到磁盘的物理文件, 而getInputStream方法会中转一次(先通过getInputStream从内存中获取流,再通过outputStream输出到磁盘物理文件)。两者相比,即使从步骤来看,你也能看出来transferTo效率更高了吧。

好啦,本篇就到此结束啦!

时间: 2024-08-02 14:52:17

深入springMVC源码------文件上传源码解析(下篇)的相关文章

eclipse中导入jdk源码、SpringMVC注解@RequestParam、SpringMVC文件上传源码解析、ajax上传excel文件

eclipse中导入jdk源码:http://blog.csdn.net/evolly/article/details/18403321, http://www.codingwhy.com/view/799.html. ------------------------------- SpringMVC注解@RequestParam:http://825635381.iteye.com/blog/2196911. --------------------------- SpringMVC文件上传源

SpringMVC实现单文件上传、多文件上传、文件列表显示、文件下载

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文详细讲解了SpringMVC实例单文件上传.多文件上传.文件列表显示.文件下载. 一.新建一个Web工程,导入相关的包 springmvc的包+commons-fileupload.jar+connom-io.jar+commons-logging,jar+jstl.jar+standard.jar 整个相关的包如下: 整个工程目录如下: 二.配置web.xml和SpringMVC文件

springMVC + hadoop + httpclient 文件上传请求直接写入hdfs

springMVC + hadoop + httpclient 文件上传请求直接写入hdfs

【springMVC】之文件上传

通过前两篇博客的学习,想必大家对springMVC已经有了一个基本的认识.今天我们主要来学习一下springMVC两种文件上传的方式. 首先介绍第一种,通过字节流的方式实现文件上传.首先创建一个upload.jsp页面 <body> <h>添加用户</h> <!-- entype要声音和支撑这种类型的,保证文件上传不会被解码--> <!-- form表单,action是说讲这个表单提交到什么路径: method="post"是提交方

SpringMVC学习(九)——SpringMVC中实现文件上传

这一篇博文主要来总结下SpringMVC中实现文件上传的步骤.但这里我只讲单个文件的上传. 环境准备 SpringMVC上传文件的功能需要两个jar包的支持,如下: 工程中肯定要导入以上两个jar包,主要是CommonsMultipartResolver解析器依赖commons-fileupload和commons-io这两个jar包. 单个文件的上传 前台页面 我们要改造editItem.jsp页面,主要是在form表单中添加商品图片一栏,效果我截图如下: 注意一点的是form表单中别忘了写e

ajaxFileUpload 实现多文件上传(源码)

按照原ajaxFileUpload.js是不能多文件上传的.需要对源码进行修改:主要修改了fileElementId部分 具体参考 https://blog.csdn.net/itmyhome1990/article/details/36396291,里面有具体使用方法 下面是修改后的源码 jQuery.extend( { handleError : function(s, xhr, status, e) { if (s.error) s.error(xhr, status, e); else

【SpringMVC学习08】SpringMVC中实现文件上传

之前有写过一篇struts2实现的文件上传,这一篇博文主要来总结下springmvc实现文件上传的步骤.首先来看一下单个文件的上传,然后再来总结下多个文件上传. 1. 环境准备 springmvc上传文件的功能需要两个jar包的支持(点我下载),如下 2. 单个文件的上传 2.1 前台页面 简单的写一下前台页面,注意一点的是form表单中别忘了写enctype="multipart/form-data"属性: <tr> <td>商品图片</td> &

springMVC之单文件上传

springmvc对form表单的支持已经很成熟,仅仅两步: 配置支持文件上传的bean:org.springframework.web.multipart.commons.CommonsMultipartResolver  同时设置文件编码.单次提交表单上传文件最大字节数.以及写文件时使用的缓冲区大小 开发处理:handler[org.hy.ssm.web.controller.FormController.uploadOne(MultipartFile)]  Java代码   /** * @

SpringMVC 4:文件上传和校验

文件上传,配置文件上传解析器 Xml代码   <!-- 文件上传解析器  上传最大尺寸:10M(10485760).id名称必须为:multipartResolver -->       <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">           <property n