• Spring MVC 使用介绍(十四)文件上传下载


    一、概述

    文件上传时,http请求头Content-Type须为multipart/form-data,有两种实现方式:

    1、基于FormData对象,该方式简单灵活

    2、基于<form>表单元素,method设为POST,enctype设置为multipart/form-data,在form表单上提交

    web容器收到该请求时,须根据请求头将字节流解析为文件对象,spring mvc 提供了MultipartResolver、MultipartFile两个接口用于支持文件上传功能

    二、MultipartResolver & MultipartFile

    1、MultipartResolver接口提供了文件解析功能,其定义如下:

    public interface MultipartResolver {
        boolean isMultipart(HttpServletRequest request);
        MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
        void cleanupMultipart(MultipartHttpServletRequest request);
    }

    Spring MVC使用Apache Commons fileupload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver

    2、MultipartFile接口代表上传的文件,提供了文件操作的相关功能,其定义如下:

    public interface MultipartFile {
        String getName();
        String getOriginalFilename(); // 原文件名
        String getContentType();
        boolean isEmpty();
        long getSize();
        byte[] getBytes() throws IOException;
        InputStream getInputStream() throws IOException; // 获取文件流
        void transferTo(File dest) throws IOException, IllegalStateException; // 保存文件
    }

    三、使用示例

    1、添加pom依赖

    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>

    2、spring-mvc.xml中配置MultipartResolver

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 上传文件大小限制,单位为字节-10Mb -->
        <property name="maxUploadSize">
            <value>10485760</value>
        </property>
        <!-- 请求的编码格式 -->
        <property name="defaultEncoding">
            <value>UTF-8</value>
        </property>
    </bean>

    3、controller

    @Controller
    public class FileController {
        /**
         * 文件存储目录
         */
        private static final String uploadFolder = "d:/upload-file/";
    
        /**
         * 上传
         */
        @ResponseBody
        @RequestMapping(value = "/upload", method = RequestMethod.POST)
        public Map<String, String> fileUpload(@RequestParam("file") MultipartFile file, @RequestParam("fileType") String fileType) throws Exception {
            
            // 创建文件目录
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
            String dateStr = sdf.format(new Date());
            String dirPath = uploadFolder + dateStr;
            File dir = new File(dirPath);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            
            // 保存文件
            String fileName = UUID.randomUUID() + this.getFileNameSuffix(file.getOriginalFilename());
            File savedFile = new File(dir + "/" + fileName);
            file.transferTo(savedFile);
            
            Map<String, String> result = new HashMap<String, String>();
            result.put("fileName", file.getOriginalFilename());
            result.put("fileType", fileType);
            result.put("filePath", dateStr + "/" + fileName);
            
            return result;
        }
        
        /**
         * 下载
         */
        @RequestMapping("/download")
        public void downloadFile(@RequestParam(value = "fileName", required = false) String fileName, @RequestParam("filePath") String filePath, HttpServletResponse response) throws Exception {
            File file = new File(uploadFolder + filePath);
            if (!file.exists()) {
                return;
            }
            
            // 读取字节流到缓存
            InputStream in = new BufferedInputStream(new FileInputStream(file));
            byte[] buffer = new byte[in.available()];
            in.read(buffer);
            in.close();
            
            // 设置ContentType
            String suff = this.getFileNameSuffix(file.getName());
            if (suff != null) {
                suff = suff.toLowerCase();
            }
            switch (suff) {
                case ".jpg":
                case ".jpeg":
                    response.setContentType(MediaType.IMAGE_JPEG_VALUE);
                    break;
                case ".png":
                    response.setContentType(MediaType.IMAGE_PNG_VALUE);
                    break;
                case ".gif":
                    response.setContentType(MediaType.IMAGE_GIF_VALUE);
                    break;
                default:
                    response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
                    break;
            }
            
            // 输出
            response.addHeader("Content-Disposition", "attachment;filename="" + (StringUtils.isEmpty(fileName) ? file.getName() : fileName));
            response.addHeader("Content-Length", "" + file.length());
            response.setContentType("application/x-msdownload");
            OutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(buffer);
            out.flush();
        }
        
        /**
         * 获取文件名后缀*/
        private String getFileNameSuffix(String fileName) {
            
            if (StringUtils.isEmpty(fileName)) {
                return null;
            }
            
            String suffix;
            int index = fileName.lastIndexOf(".");
            if (index == -1) {
                suffix = null;
            }
            else {
                suffix = fileName.substring(index);
            }
            
            return suffix;
        }
    }

    4、前端页面(test.html)

    <!DOCTYPE html>
    <html>
    <head>
        <title>test page</title>
    </head>
    <body>
        <div>
            <input id="upload" type="file" onchange="uploadFile(event)" />
        </div>
        <div>
            <input id="download" type="button" onclick="downloadFile()" value="下载" />
        </div>
    </body>
    <script>
        var context = '/test'
    
        function uploadFile(event) {
            // 文件类型过滤
            var file = event.currentTarget.files[0];
            var fileName = file.name;
            if (/.(jpg|jpeg|png|gif)$/i.test(fileName) == false) {
                alert('提示', '不支持的图片类型,头像只支持.jpg,.jpeg,.png,.gif');
                return;
            }
    
            var formData = new FormData();
            formData.append('fileType', 'portrait');
            formData.append('file', event.currentTarget.files[0]);
    
            // 发送请求
            var xhr = new XMLHttpRequest();
            xhr.open('POST', context + '/upload', true);
            xhr.onreadystatechange = function() {
                if (xhr.readyState==4 && xhr.status==200) {
                    document.getElementById('download').fileData = JSON.parse(xhr.responseText);
                    alert("上传成功");
                }
            }
            xhr.send(formData);
    
            // 重置文件上传控件,使得重复选择同一个文件时,onchange依旧触发
            event.target.value = null;
        }
    
        function downloadFile() {
            var fileData = document.getElementById('download').fileData;
            location.href = context + '/download?filePath=' + decodeURIComponent(fileData.filePath) + '&fileName=' + decodeURIComponent(fileData.fileName);
        }
    </script>
    </html>

    访问http://file-test/test.html,可测试文件的上传下载功能

    补充:示例使用@ResponseBody输出json数据,因此还需添加相关配置,详细可参考Spring MVC 使用介绍(五)—— 注解式控制器(一):基本介绍

    另外,文件下载除了示例中文件流方式,还可以基于spring对静态文件的支持功能,详细可参考Spring MVC 使用介绍(十一)—— 跨域与静态资源访问

    参考:

    Spring MVC的文件上传

  • 相关阅读:
    git爬坑不完全指北(二):failed to push some refs to ‘XXX’的解决方案
    javascript精雕细琢(三):作用域与作用域链
    javascript精雕细琢(二):++、--那点事
    git爬坑不完全指北(一):Permission to xxx.git denied to user的解决方案
    深入浅出CSS(三):隐藏BOSS大盘点之默认属性小总结
    读书笔记
    MPP5运维手册
    HTML自闭合(self-closing)标签
    Mysql JDBC的通信协议(报文的格式和基本类型)
    详解 & 0xff 的作用
  • 原文地址:https://www.cnblogs.com/MattCheng/p/10374203.html
Copyright © 2020-2023  润新知