Java小强个人技术博客站点    手机版
当前位置: 首页 >> Java >> SpringMVC 文件下载的两种方式

SpringMVC 文件下载的两种方式

22751 Java | 2022-7-29

现如今又要写一个关于下午的业务,因此再次编写测试用例,总结了两种下载方式。

首先介绍一个14年就写的文章,Http断点下载实简单讲解(http://www.javacui.com/java/98.html )


基于Spring的ResponseEntity

/**
 * SpringMVC方式
 */
@GetMapping("/downSpring")
public ResponseEntity<byte[]> down() throws Exception {
    if(System.getProperty("os.name").contains("Windows")){
        System.out.println("操作系统Windows");
    }else{
        System.out.println("操作系统Linux");
    }
    String filePath = "D:\\temp\\SGWSRootCA.jks";
    byte [] body = null;
    try {
        InputStream in = new FileSystemResource(filePath).getInputStream();
        body = new byte[in.available()];
        in.read(body);
    } catch (IOException e) {
        throw e;
    }
    //添加响应头
    HttpHeaders headers = new HttpHeaders();
    //这里fileName有可能出现下载文件乱码-需要自己处理
    headers.add("Content-Disposition", "attachment;filename=SGWSRootCA.jks");
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    HttpStatus statusCode = HttpStatus.OK;
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(body, headers, statusCode);
    return responseEntity;
}

Java通用下载方式

/**
 * 普通HTTP流输出方式
 */
@GetMapping("/downJava")
public void downJava(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String filePath = "D:\\temp\\SGWSRootCA.jks";
    //设置响应头和客户端保存文件名
    response.setCharacterEncoding("utf-8");
    response.setContentType("multipart/form-data");
    response.setHeader("Content-Disposition", "attachment;fileName=SGWSRootCA.jks");
    try {
        //打开本地文件流
        InputStream inputStream = new FileInputStream(filePath);
        OutputStream os = response.getOutputStream();
        byte[] b = new byte[2048];
        int length;
        while ((length = inputStream.read(b)) > 0) {
            os.write(b, 0, length);
        }
        // 这里主要关闭。
        os.close();
        inputStream.close();
    } catch (Exception e){
        throw e;
    }finally {
        // 是否删除原文件在这里操作
    }
}


那么我们再基于第二种方式,写一个功能,就是网页引用一张图片,这个图片是二维码,具体二维码内容是什么,是后台程序控制。

这里生成本博客访问的二维码来编写测试代码。

package com.example.springboot.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;

@RestController
@RequestMapping("/test/file")
public class FileController {

    /**
     * 获得博客地址二维码的图片
     */
    @GetMapping("/getMyBlogQr")
    public void getMyBlogQr(HttpServletResponse response){
        // 组装下载文件路径,区分Linux和Win
        String filePath;
        String fileName = "javacui.jpg";
        if(System.getProperty("os.name").contains("Windows")){
            filePath = "D:\\temp\\" + fileName;
        }else{
            filePath = "/home/" + fileName;
        }
        // 生成二维码,存在就不用再次生成
        // 生成指定url对应的二维码到文件,宽和高都是300像素
        if (!new File(filePath).exists())
            QrCodeUtil.generate("http://www.javacui.com/",
                    300, 300, FileUtil.file(filePath));
        //设置响应头和客户端保存文件名
        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
        response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
        try {
            //打开本地文件流
            InputStream inputStream = new FileInputStream(filePath);
            OutputStream os = response.getOutputStream();
            byte[] b = new byte[512];
            int length;
            while ((length = inputStream.read(b)) > 0) {
                os.write(b, 0, length);
            }
            // 这里主要关闭。
            os.close();
            inputStream.close();
        } catch (Exception e){
        }
    }

}


然后在页面使用一个图片标签,指向这个接口即可

<img src="http://localhost:1088/test/file/getMyBlogQr" style="width: 300px; height: 300px;">


打开HTML页面可以看到:

javacui.jpg


基于ResponseEntity的实现的局限性还是很大,从代码中可以看出这种下载方式是一种一次性读取的下载方式,在文件较大的时候会直接抛出内存溢出。

还有就是这种方式在进行下载统计的时候也存在局限性,无法统计在下载失败的情况已完成下载量,因此限制了对下载的功能扩展。

虽然这种实现方式有局限性,但是也有着优点,简洁。在很多时候我们并不需要那么复杂的下载功能时,这种实现就应该是首选了。


然而下载java通用实现在功能上比第一种实现更加丰富,对下载的文件大小无限制(循环读取一定量的字节写入到输出流中,因此不会造成内存溢出,但是在下载人数过多的时候应该还是出现一些异常,较大文件时可以采用其他策略)。

另外因为是这种实现方式是基于循环写入的方式进行下载,在每次将字节块写入到输出流中的时都会进行输出流的合法性检测,在因为用户取消或者网络原因造成socket断开的时候,系统会抛出SocketWriteException,系统可以捕捉这个过程中抛出的异常,当捕捉到异常的时候我们可以记录当前已经传输的数据量,这样就可以完成下载状态和对应状态下载量和速度之类的数据记录。

另外这种方式实现方式还可以实现一种断点续载的功能。


推荐您阅读更多有关于“ 下载 springmvc 断点续传 springboot ”的文章

上一篇:Kafka 消费端消费重试和死信队列 下一篇:Java中双重检查锁(double checked locking)

猜你喜欢

发表评论:

评论:

回复 Java小强 评论于 2022-07-29 11:03
生成二维码的工具有很多,这里直接用工具箱:二维码工具-QrCodeUtil:https://hutool.cn/docs/#/extra/%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%B7%A5%E5%85%B7-QrCodeUtil