SpringMVC 文件上传和下载

SpringMVC 文件上传和下载

介绍

文件上传和下载是一个十分常见的应用场景,而这篇文章是一篇基础的文章
如果需要使用 MinIO 就只需要操作服务端文件换成输入流
MinIO使用:https://www.lldwb.top/archives/5639

文件上传

配置方式

原生MVC

注意:有兼容性问题,在 Tomcat 下没问题,但是在 Jetty 下会出错
比如使用如下文件VO类会出现报错,但是支持直接接收 MultipartFile 类型

@Data
public class ProductVO {
    /**
     * SpringMVC 封装的上传附件对象
     * 图片
     */
    private MultipartFile file;
}
Web.xml 设置
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 开启文件上传(兼容性不好) -->
    <multipart-config/>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

第三方

Maven 依赖

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

案例

单文件

/**
 * 添加商品,同时带有上传的附件
 *
 * @return
 */
@PostMapping("/addFile")
public ResultVo add(ProductVO productVO) throws IOException {
    ResultVo vo = new ResultVo();
    // 获取上传的路径(上传到本地图片、图片服务器)
    // 绝对路径
    String uploadPath = "C:\\Users\\32471\\Pictures\\Saved Pictures";
    // 获取上传的文件名
    MultipartFile multipartFile = productVO.getFile();
    // 根据路径构建一个上传的文件对象
    File upladFile = new File(uploadPath);
    // 判断路径中的文件夹是否存在,不存在则创建出来
    if (!upladFile.exists()) {
        // 创建文件夹
        upladFile.mkdirs();
    }

    // 目标的文件名
    String fileName = multipartFile.getOriginalFilename();
    log.info("目标的文件名" + fileName);
    log.info("name:" + multipartFile.getName());
    // 文件路径
    log.info(uploadPath);

    // 执行上传
    Path path = FileSystems.getDefault().getPath(upladFile.getAbsolutePath(), fileName);
    multipartFile.transferTo(path);

    return vo;
}
@Data
public class ProductVO {
    /**
     * 名称
     */
    private String name;
    /**
     * SpringMVC 封装的上传附件对象
     * 图片
     */
    private MultipartFile file;
}
<!DOCTYPE html>
<html lang="en">
    <head>
        <script src="js/jquery.min.js"></script>
        <script src="js/vue.js"></script>
        <script src="js/vue-router.js"></script>
        <!-- 引入样式 -->
        <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
        <!-- 引入组件库 -->
        <script src="https://unpkg.com/element-ui/lib/index.js"></script>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <div id="msg" style="color: red"></div>
        <form id="f1" enctype="multipart/form-data">
            Nmae:<input type="text" name="name" />
            文件:<input type="file" name="file" />
            <input type="button" value="提交">
        </form>
    </body>
    <script>
        $(() => {
            $(':button').on('click', () => {
                // 构建 formData 对象
                let formData = new FormData($('#f1')[0]);

                $.ajax({
                    url: 'addFiles',
                    type: 'post',
                    data: formData,
                    success(result) {
                        if (result.code == 500) {
                            $('#msg').empty();
                            let errors = result.message;
                            errors = $.parseJSON(errors);
                            // $.each(errors, (key, value) => {
                            value = "错误"
                            $('#msg').append(value + '<br/>')
                            // })
                        }else if(result.code == 200){
                            value = "成功了"
                            $('#msg').append(value + '<br/>')
                        }
                    },
                    // 告诉 jquery 不要处理发送的数据类型
                    processData: false,
                    // 告诉 jquery 不要设置请求头的 content-Type
                    contentType: false
                })
            })
        })
    </script>
</html>

多文件

@PostMapping("/addFiles")
public ResultVo adds(ProductVOs productVOs) throws IOException {
    // 获取上传的路径(上传到本地图片、图片服务器)
    // 绝对路径
    String uploadPath = "C:\\Users\\32471\\Pictures\\Saved Pictures";
//        MultipartFile multipartFile = file;
    // 根据路径构建一个上传的文件对象
    File upladFile = new File(uploadPath);
    // 判断路径中的文件夹是否存在,不存在则创建出来
    if (!upladFile.exists()) {
        // 创建文件夹
        upladFile.mkdirs();
    }

    for (MultipartFile multipartFile : productVOs.getFile()) {
        // 目标的文件名
        log.info("name:" + multipartFile.getName());
        // 文件路径
        log.info(uploadPath);

        // 执行上传
        multipartFile.transferTo(FileSystems.getDefault().getPath(upladFile.getAbsolutePath(), multipartFile.getOriginalFilename()));
    }

    ResultVo vo = new ResultVo();
    return vo;
}
@Data
public class ProductVOs {
    /**
     * 名称
     */
    private String name;
    /**
     * SpringMVC 封装的上传附件对象
     * 图片
     */
    private MultipartFile[] file;
}
<!DOCTYPE html>
<html lang="en">
    <head>
        <script src="js/jquery.min.js"></script>
        <script src="js/vue.js"></script>
        <script src="js/vue-router.js"></script>
        <!-- 引入样式 -->
        <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
        <!-- 引入组件库 -->
        <script src="https://unpkg.com/element-ui/lib/index.js"></script>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <div id="msg" style="color: red"></div>
        <form id="f1" enctype="multipart/form-data">
            Nmae:<input type="text" name="name" />
            多文件:<input type="file" name="file" multiple/>
            <input type="button" value="提交">
        </form>
    </body>
    <script>
        $(() => {
            $(':button').on('click', () => {
                // 构建 formData 对象
                let formData = new FormData($('#f1')[0]);

                $.ajax({
                    url: 'addFiles',
                    type: 'post',
                    data: formData,
                    success(result) {
                        if (result.code == 500) {
                            $('#msg').empty();
                            let errors = result.message;
                            errors = $.parseJSON(errors);
                            // $.each(errors, (key, value) => {
                            value = "错误"
                            $('#msg').append(value + '<br/>')
                            // })
                        }else if(result.code == 200){
                            value = "成功了"
                            $('#msg').append(value + '<br/>')
                        }
                    },
                    // 告诉 jquery 不要处理发送的数据类型
                    processData: false,
                    // 告诉 jquery 不要设置请求头的 content-Type
                    contentType: false
                })
            })
        })
    </script>
</html>

文件下载

@GetMapping("/download")
public ResponseEntity<InputStreamResource> download(String fileName) throws Exception {
    // 获取服务器的路径(上传到本地图片、图片服务器)
    // 绝对路径
    String downloadPath = "C:\\Users\\32471\\Pictures\\Saved Pictures\\"+fileName;
    // 构建文件输入流读取服务器上的文件
    FileInputStream inputStream = new FileInputStream(downloadPath);

    // 对文件名进行编码,防止在响应头中出现乱码
    fileName = URLEncoder.encode(fileName,"UTF-8");

    // 设置响应头,告诉浏览器响应的是流数据
    HttpHeaders headers = new HttpHeaders();
    // 设置头信息,将响应内容处理的方式设置为附件下载
    headers.setContentDispositionFormData("attachment",fileName);
    // 设置响应类型为流类型
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

    // 创建 InputStreamReader 对象封装输入流,用于读取服务器文件
    InputStreamResource inputStreamReader = new InputStreamResource(inputStream);
    // 创建 ResponseEntity 对象,封装(InputStreamReader,响应头 HttpHeaders,状态码 201)
    // 200 成功,201 成功但是还有数据
    ResponseEntity<InputStreamResource> response = new ResponseEntity<>(inputStreamReader,headers,HttpStatus.CREATED);
    return response;
}

上传到 MinIo

@PostMapping("/addFiles")
public ResultVo adds(ProductVOs productVOs) throws Exception {
    //获取 minioClient
    MinioClient minioClient = ApplicationContextHolder.getBean(MinioFactoryBean.class).getObject();
    // 遍历多个文件
    for (MultipartFile multipartFile : productVOs.getFile()) {
        // 获取文件的输入流
        InputStream inputStream = multipartFile.getInputStream();
       // 上传输入流到 MinIO
minioClient.putObject(PutObjectArgs.builder().bucket("test").object(multipartFile.getOriginalFilename()).stream(inputStream, multipartFile.getSize(), -1).contentType(multipartFile.getContentType()).build());
    }

    ResultVo vo = new ResultVo();
    return vo;
}

从 MinIO 下载

@GetMapping("/download")
public ResponseEntity<InputStreamResource> download(String fileName) throws Exception {

    //获取 minioClient
    MinioClient minioClient = ApplicationContextHolder.getBean(MinioFactoryBean.class).getObject();
    // 从 MinIO 获取输入流
    InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket("test").object(fileName).build());

    // 对文件名进行编码,防止在响应头中出现乱码
    fileName = URLEncoder.encode(fileName, "UTF-8");
    // 设置响应头,告诉浏览器响应的是流数据
    HttpHeaders headers = new HttpHeaders();
    // 设置头信息,将响应内容处理的方式设置为附件下载
    headers.setContentDispositionFormData("attachment", fileName);
    // 设置响应类型为流类型
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

    // 创建 InputStreamReader 对象封装输入流,用于读取服务器文件
    InputStreamResource inputStreamReader = new InputStreamResource(inputStream);
    // 创建 ResponseEntity 对象,封装(InputStreamReader,响应头 HttpHeaders,状态码 201)
    // 200 成功,201 成功但是还有数据
    ResponseEntity<InputStreamResource> response = new ResponseEntity<>(inputStreamReader, headers, HttpStatus.CREATED);
    return response;
}