首页 > 其他分享 >SpringBoot返回文件让前端下载的几种方式

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

时间:2024-07-08 16:52:00浏览次数:18  
标签:文件 outputStream SpringBoot buffer 前端 几种 file new response

0x01 背景

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

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

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

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

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

    @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的问题

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

    @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);
        }

    }

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

    @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);
        }
        
    }

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

    @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);
        }

    }

0x05 总结

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

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

标签:文件,outputStream,SpringBoot,buffer,前端,几种,file,new,response
From: https://www.cnblogs.com/Fzeng/p/18290313

相关文章

  • 前端面试基础html/js/css
    一、css1.说一下css盒子模型CSS盒子模型(BoxModel)是CSS中用于描述元素尺寸和布局的一个重要概念。它定义了元素的内容、内边距、边框、外边距和高度的计算方式。盒子模型对于网页布局和响应式设计至关重要。在CSS中,每个元素都可以被视为一个盒子,这个盒子由内容(content)、......
  • web前端热门面试题一
    JavaScript中的数据类型有哪些?并谈谈它们在存储上的差别。JavaScript中的数据类型及存储差别数据类型JavaScript中的数据类型主要可以分为两大类:基本数据类型(也称为原始数据类型)和引用数据类型。具体分类如下:基本数据类型Number:数字类型,包括整数和浮点数。JavaScript内......
  • SpringBoot集成Kafka快速启动示例
    源码地址:https://github.com/ghdefe/kafka-demokafka消息发送接收示例1.启动kafka实例cat<<EOF>docker-compose.ymlversion:"2"services:zookeeper:image:docker.io/bitnami/zookeeper:3.8ports:-"2181:2181"-"......
  • 免费分享一套SpringBoot+Vue超市(进销存,收银,积分)管理系统【论文+源码+SQL脚本】,帅呆
    大家好,我是java1234_小锋老师,看到一个不错的SpringBoot+Vue超市(进销存,收银,积分)管理系统,分享下哈。项目介绍本论文设计并实现了一套基于SpringBoot、Vue和MySQL的超市管理系统。该系统旨在通过现代化的Web应用技术提升超市管理效率和用户体验。首先,通过详细的需求分析和功......
  • java springboot监听事件和处理事件
    在SpringBoot中,监听和处理事件是一种常用的模式,用于在应用程序的不同部分之间传递信息。Spring的事件发布/订阅模型允许我们创建自定义事件,并在这些事件发生时由注册的监听器进行处理。这里,我将提供一个简单的SpringBoot应用程序示例,其中将包括事件的定义、事件的发布以及事件......
  • javab毕业设计-基于Java的校园二手交易商城系统设计与实现,基于springboot+vue二手跳蚤
    文章目录背景介绍演示视频(进入B站观看画面更清晰):项目架构和内容获取(文末获取)部分功能展示用户前端系统管理后台项目相关文件为什么我?本章节给大家带来的是一个基于java的大学生二手交易平台系统设计与实现,可适用于校园二手交易系统,基于Java的二手交易商城系统,大学......
  • 前端??和||的区别
    constuser={profile:{name:'张三'}};constuserName=user.profile?.name??'匿名';//结果:userName='张三'此代码首先演示了如何使用可选链运算符(?.)安全地访问user.profile的name值。如果user.profile是undefined或null,它会短路并返回undefined,从而避......
  • List 按照指定大小分片的几种方式
    如果有一个list<string>里面可能有1000份或者更多数据,如果需要进行入库等操作,需要拆分成指定大小每份进行处理,这种需求很常见,那么应该怎么做呢?首先我们需要将List<String> 转为多份后进行逐个处理, 处理批量事务注意事务哦 那么怎么将list转为多份呢? 下面介绍2......
  • springboot在线商城系统源码idea开发mysql数据库
    下载地址:https://download.csdn.net/download/qq_41221322/89519994摘要近年来,网上购物成了风靡全球的一种现象,大家逐渐接受了网上下单,隔天取货的这种方式。我们分析了一些购物网站现有的不足,结合我们所学的知识,制作了一款购物网站程序。学以致用,语言采用了大学时期学习的J......
  • springboot在线智能助考系统-计算机毕业设计源码00068
    摘要随着人工智能技术的快速发展,智能辅助学习系统在教育领域日益受到重视。本研究旨在基于GPT构建在线智能助考系统,结合先进的自然语言处理技术,为用户提供智能问答、模拟考试、资源分享、交流论坛等功能,旨在提升用户学习效率和体验。GPT模型作为一种自然语言生成模型,具有......