• 最全面的Spring教程(五)——文件上传与下载


    前言

    在这里插入图片描述

    本文为 【SpringMVC教程】文件上传与下载 相关知识,具体将对使用MultipartResolver处理文件上传的步骤,两种文件下载方式(直接向response的输出流中写入对应的文件流、使用 ResponseEntity来向前端返回文件)等进行详尽介绍~

    📌博主主页:小新要变强 的主页
    👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
    👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~
    👉Java微服务开源项目可参考:企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)

    ↩️本文上接:最全面的SpringMVC教程(四)——Controller 与 RestFul


    目录

    在这里插入图片描述

    文件上传是项目开发中最常见的功能之一 ,SpringMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。

    前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。

    <form action="" enctype="multipart/form-data" method="post">
        <input type="file" name="file"/>
        <input type="submit">
    form>
    
    • 1
    • 2
    • 3
    • 4

    表单中enctype属性的详细说明:

    • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
    • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
    • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

    一旦设置了enctypemultipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。

    • Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。而Spring MVC则提供了更简单的封装。
    • Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
    • Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。

    一、文件上传

    【MultipartResolver】用于处理文件上传。当收到请求时,DispatcherServlet 的 checkMultipart() 方法会调用 MultipartResolver 的 isMultipart() 方法判断请求中【是否包含文件】。如果请求数据中包含文件,则调用 MultipartResolver 的 resolveMultipart() 方法对请求的数据进行解析,然后将文件数据解析成 MultipartFile 并封装在 MultipartHttpServletRequest (继承了 HttpServletRequest) 对象中,最后传递给 Controller。

    我们可以看到DispatcherServlet的核心方法中第一句就是如下的代码:

    try {
        processedRequest = checkMultipart(request);
        multipartRequestParsed = (processedRequest != request);
        ...
    
    • 1
    • 2
    • 3
    • 4

    注意: MultipartResolver 默认不开启,需要手动开启。

    文件上传对前端表单有如下要求:为了能上传文件,必须将表单的【method设置为POST】,并将enctype设置为【multipart/form-data】。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。

    对表单中的 enctype 属性的详细说明:

    • application/x-www-form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
    • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
    <form action="" enctype="multipart/form-data" method="post">
       <input type="file" name="file"/>
       <input type="submit">
    form>
    
    • 1
    • 2
    • 3
    • 4

    一旦设置了enctypemultipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。

    🍀(1)导入这个【commons-fileupload】jar包,Maven会自动帮我们导入它的依赖包【commons-io】

    
    <dependency>
       <groupId>commons-fileuploadgroupId>
       <artifactId>commons-fileuploadartifactId>
       <version>1.3.3version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🍀(2)配置bean:multipartResolver

    注意: 这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!

    
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
       
       <property name="defaultEncoding" value="utf-8"/>
       
       <property name="maxUploadSize" value="10485760"/>
       <property name="maxInMemorySize" value="40960"/>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    CommonsMultipartFile 的常用方法:

    • String getOriginalFilename():获取上传文件的原名
    • InputStream getInputStream():获取文件流
    • void transferTo(File dest):将上传文件保存到一个目录文件中

    🍀(3)编写前端页面

    <form action="/upload" enctype="multipart/form-data" method="post">
     <input type="file" name="file"/>
     <input type="submit" value="upload">
    form>
    
    • 1
    • 2
    • 3
    • 4

    🍀(4)编写Controller类

    package com.wang.controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.multipart.commons.CommonsMultipartFile;
    import javax.servlet.http.HttpServletRequest;
    import java.io.*;
    @Controller
    public class FileController {
        //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
        //批量上传CommonsMultipartFile则为数组即可
        @RequestMapping("/upload")
        public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
            //获取文件名 : file.getOriginalFilename();
            String uploadFileName = file.getOriginalFilename();
            //如果文件名为空,直接回到首页!
            if ("".equals(uploadFileName)){
                return "redirect:/index.jsp";
            }
            System.out.println("上传文件名 : "+uploadFileName);
            //上传路径保存设置
            String path = request.getServletContext().getRealPath("/upload");
            //如果路径不存在,创建一个
            File realPath = new File(path);
            if (!realPath.exists()){
                realPath.mkdir();
            }
            System.out.println("上传文件保存地址:"+realPath);
            InputStream is = file.getInputStream(); //文件输入流
            OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流
            //读取写出
            int len=0;
            byte[] buffer = new byte[1024];
            while ((len=is.read(buffer))!=-1){
                os.write(buffer,0,len);
                os.flush();
            }
            os.close();
            is.close();
            return "redirect:/index.jsp";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    🍀(5)测试上传文件

    🍀(6)采用file.Transto 来保存上传的文件

    编写Controller类:

    /*
     * 采用file.Transto 来保存上传的文件
     */
    @RequestMapping("/upload2")
    public String  fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        //上传文件地址
        System.out.println("上传文件保存地址:"+realPath);
        //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
        file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
        return "redirect:/index.jsp";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    小知识: 我们在文件上传可以考虑以下几点:

    • (1)文件的原始信息,或者叫文件的元数据是不是可以存在数据库,具体应该怎么做?
    • (2)文件的上传目录能不能写在配置文件当中,这个应该怎么做?
    • (3)文件上传到服务器后可不可以安装一定的规则分目录存储,比如日期?
    • (4)思考怎么使用阿里云的oss进行图片存储?

    二、文件下载

    • 第一种可以直接向response的输出流中写入对应的文件流
    • 第二种可以使用 ResponseEntity来向前端返回文件

    1️⃣传统方式

    文件下载步骤:

    • (1)设置 response 响应头
    • (2)读取文件 — InputStream
    • (3)写出文件 — OutputStream
    • (4)执行操作
    • (5)关闭流 (先开后关)
    @GetMapping("/download1")
    @ResponseBody
    public R download1(HttpServletResponse response){
        FileInputStream fileInputStream = null;
        ServletOutputStream outputStream = null;
        try {
            // 这个文件名是前端传给你的要下载的图片的id
            // 然后根据id去数据库查询出对应的文件的相关信息,包括url,文件名等
            String  fileName = "wang.jpg";
    
            //1、设置response 响应头,处理中文名字乱码问题
            response.reset(); //设置页面不缓存,清空buffer
            response.setCharacterEncoding("UTF-8"); //字符编码
            response.setContentType("multipart/form-data"); //二进制传输数据
            //设置响应头,就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名。
            //Content-Disposition属性有两种类型:inline 和 attachment 
            //inline :将文件内容直接显示在页面 
            //attachment:弹出对话框让用户下载具体例子:
            response.setHeader("Content-Disposition",
                               "attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
    
    		// 通过url获取文件
            File file = new File("D:/upload/"+fileName);
            //2、 读取文件--输入流
            fileInputStream = new FileInputStream(file);
            //3、 写出文件--输出流
            outputStream = response.getOutputStream();
    
            byte[] buffer = new byte[1024];
            int len;
            //4、执行写出操作
            while ((len = fileInputStream.read(buffer)) != -1){
                outputStream.write(buffer,0,len);
                outputStream.flush();
            }
    
            return R.success();
        } catch (IOException e) {
            e.printStackTrace();
            return R.fail();
        }finally {
            if( fileInputStream != null ){
                try {
                    // 5、关闭输入流
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if( outputStream != null ){
                try {
                    // 5、关闭输出流
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    2️⃣使用ResponseEntity方式

    @GetMapping("/download2")
    public ResponseEntity<byte[]> download2(){
        try {
            String fileName = "wang.jpg";
            byte[] bytes = FileUtils.readFileToByteArray(new File("D:/upload/"+fileName));
            HttpHeaders headers=new HttpHeaders();
            // Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名。
            headers.set("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));
            headers.set("charsetEncoding","utf-8");
            headers.set("content-type","multipart/form-data");
            ResponseEntity<byte[]> entity=new ResponseEntity<>(bytes,headers, HttpStatus.OK);
            return entity;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    后记

    在这里插入图片描述
    👉Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~
    👉算法刷题路线可参考:算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~

  • 相关阅读:
    猿如意 | 带你手把手安装 Visual Studio Code
    含文档+PPT+源码等]精品微信小程序ssm超市购物系统小程序+后台管理系统|前后分离VUE[包运行成功]微信小程序项目源码Java毕业设计
    【无标题】
    使用Java做业务开发,如何做好一个定时任务的技术选型?
    vs2010 c++ 解析 json(jsoncpp)
    【开源三方库】Fuse.js:强大、轻巧、零依赖的模糊搜索库
    vscode markdown 使用技巧 -- 如何快速打出一个Tab 或多个空格
    摸鱼大数据——Hive表操作——分区表
    基站天线效率相关技术研究
    HTML+CSS网页设计期末课程大作业:【中国传统文化——古诗词】学生网页设计作品 dreamweaver作业静态HTML网页设计
  • 原文地址:https://blog.csdn.net/qq_42146402/article/details/128045997