Java

当前位置:首页 > Java

SpringMVC 文件下载的两种方式 - Java

现如今又要写一个关于下午的业务,因此再次编写测试用例,总结了两种下载方式。首先介绍一个14年就写的文章,Http断点下载实简单讲解(http://www.javacui.com/java/98.html ...

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

首先介绍一个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,系统可以捕捉这个过程中抛出的异常,当捕捉到异常的时候我们可以记录当前已经传输的数据量,这样就可以完成下载状态和对应状态下载量和速度之类的数据记录。

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


上一篇:Java中双重检查锁(double checked locking)

下一篇:已经是最后一篇

相关内容

文章评论

表情

共 0 条评论,查看全部
  • 这篇文章还没有收到评论,赶紧来抢沙发吧~

评论排行榜

热门标签