SpringBoot返回文件让前端下载的几种方式

 更新时间:2025年12月01日 11:47:32   作者:N***p365  
文章介绍了开发中文件下载的两种常见解决方案,并详细描述了通过后端进行下载的原理和步骤,包括一次性读取到内存和分块写入响应输出流两种方法,此外,还提供了从网络上获取文件和文本并下载的示例,并总结了InputStream和OutputStream的区别,感兴趣的朋友跟随小编一起看看吧

01 背景

在后端开发中,通常会有文件下载的需求,常用的解决方案有两种:

  1. 不通过后端应用,直接使用nginx直接转发文件地址下载(适用于一些公开的文件,因为这里不需要授权)
  2. 通过后端进行下载,同时进行一些业务处理

本篇主要以方法2进行介绍,方法2的原理步骤如下:

  1. 读取文件,得到文件的字节流
  2. 将字节流写入到响应输出流中

02 一次性读取到内存,通过响应输出流输出到前端

    @GetMapping("/file/download")
    public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new BusinessException("当前下载的文件不存在,请检查路径是否正确");
        }
        // 将文件写入输入流
        try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
            // 一次性读取到内存中
            byte[] buffer = new byte[is.available()];
            int read = is.read(buffer);
            // 清空 response
            response.reset();
            response.setCharacterEncoding("UTF-8");
            // Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
            // attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
            // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
            // 告知浏览器文件的大小
            response.addHeader("Content-Length", "" + file.length());
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            outputStream.write(buffer);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

适用于小文件,如果文件过大,一次性读取到内存中可能会出现oom的问题

02 将文件流通过循环写入到响应输出流中(推荐)

    @GetMapping("/file/download")
    public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new BusinessException("当前下载的文件不存在,请检查路径是否正确");
        }
        // 清空 response
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
        response.setContentType("application/octet-stream");
        // 将文件读到输入流中
        try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            byte[] buffer = new byte[1024];
            int len;
            //从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1
            while((len = is.read(buffer)) > 0){
                outputStream.write(buffer, 0, len);
            }
            outputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

03 从网络上获取文件并返回给前端

    @GetMapping("/net/download")
    public void netDownload(HttpServletResponse response, @RequestParam("fileAddress") String fileAddress, @RequestParam("filename") String filename) {
        try {
            URL url = new URL(fileAddress);
            URLConnection conn = url.openConnection();
            InputStream inputStream = conn.getInputStream();
            response.reset();
            response.setContentType(conn.getContentType());
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
            byte[] buffer = new byte[1024];
            int len;
            OutputStream outputStream = response.getOutputStream();
            while ((len = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, len);
            }
            inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

04 从网络上获取文本并下载到本地

    @GetMapping("/netDownloadLocal")
    public void downloadNet(@RequestParam("netAddress") String netAddress, @RequestParam("filepath") String filepath) {
        try {
            URL url = new URL(netAddress);
            URLConnection conn = url.openConnection();
            InputStream inputStream = conn.getInputStream();
            FileOutputStream fileOutputStream = new FileOutputStream(filepath);
            int byteread;
            byte[] buffer = new byte[1024];
            while ((byteread = inputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, byteread);
            }
            fileOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

05 总结

一定要搞清楚InputStreamOutputStream的区别,如果搞不清楚的,可以和字符流进行映射,InputStream -> Reader,OutPutStream -> Writer,换成这样你就知道读取内容需要使用Reader,写入需要使用Writer了。

返回给前端的是输出流,不需要你显示的去返回(return response;),这样会报错

到此这篇关于SpringBoot返回文件让前端下载的几种方式的文章就介绍到这了,更多相关springboot文件下载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java8 对象转Map时重复 key Duplicate key xxxx的解决

    java8 对象转Map时重复 key Duplicate key xxxx的解决

    这篇文章主要介绍了java8 对象转Map时重复 key Duplicate key xxxx的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java正则表达式_动力节点Java学院整理

    Java正则表达式_动力节点Java学院整理

    什么是正则表达式,正则表达式的作用是什么?这篇文章主要为大家详细介绍了Java正则表达式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • Java 实战项目之精美物流管理系统的实现流程

    Java 实战项目之精美物流管理系统的实现流程

    读万卷书不如行万里路,只学书上的理论是远远不够的,只有在实战中才能获得能力的提升,本篇文章手把手带你用java+SpringBoot+Vue+maven+Mysql实现一个精美的物流管理系统,大家可以在过程中查缺补漏,提升水平
    2021-11-11
  • Spring Security 单点登录简单示例详解

    Spring Security 单点登录简单示例详解

    这篇文章主要介绍了Spring Security 单点登录简单示例详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02
  • Java字符串驼峰与下换线格式转换如何实现

    Java字符串驼峰与下换线格式转换如何实现

    这篇文章主要介绍了Java字符串驼峰与下换线格式转换如何实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Springboot服务引用Nacos中新增的配置文件失败问题及解决

    Springboot服务引用Nacos中新增的配置文件失败问题及解决

    这篇文章主要介绍了Springboot服务引用Nacos中新增的配置文件失败问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • SpringBoot应用部署于外置Tomcat容器的方法

    SpringBoot应用部署于外置Tomcat容器的方法

    这篇文章主要介绍了SpringBoot应用部署于外置Tomcat容器的方法,本文分步骤给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-06-06
  • GateWay路由规则与动态路由详细介绍

    GateWay路由规则与动态路由详细介绍

    这篇文章主要介绍了GateWay路由规则与GateWay动态路由,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • Gson解析空字符串发生异常的处理方法

    Gson解析空字符串发生异常的处理方法

    最近在一个项目中遇到一个问题,当面对一些不规范的json,我们的gson解析经常会抛出各种异常导致app崩溃,通过在网上查找资料,找到了原因,这篇文章给大家介绍了一些可以采取的措施来避免这种情况,有需要的朋友们可以一起来学习学习。
    2016-11-11
  • SpringBoot HikariCP连接池详解

    SpringBoot HikariCP连接池详解

    这篇文章主要介绍了SpringBoot2.0 中 HikariCP 数据库连接池原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08

最新评论