Springboot Minio最新版大文件下载

上节我们说了Minio怎么大文件上传,我们是进行了分段上传,然后合并处理,感兴趣的可以去这篇文章,

Springboot Minio最新版大文件上传-CSDN博客

那么今天的主题就是大文件下载,再大文件就需要分段下载,也就需要前端给下载的范围,就是下面的range的参数,我们为了好测试将此字段放入了参数了,实际你可以放入header头部。

下载的Controller类:

@Slf4j
@RestController
public class DownloadController {@Resourceprivate IDownloadProcess downloadProcess;// http://localhost:8082/download?filename=a9500aa2091875f3d02a9b84ae1ab712.mp4&range=bytes=0-52428800// 分段下载的化,支持断点下载,暂停下载,断网恢复下载等。// 我测试就采取这种方式RequestParam,大家真实场景可以放到header里 @RequestHeader(name = "Range", required = false) String range,@GetMapping("/download")public ResponseEntity downloadFile(@RequestParam String filename,@RequestParam(required = false) String range,HttpServletRequest request, HttpServletResponse response) {try {return downloadProcess.downloadFile(filename, range, request, response);} catch (Exception e) {log.error("下载异常|参数:{},{}|{}", filename, range, e);return new ResponseEntity<byte[]>(HttpStatus.INTERNAL_SERVER_ERROR);}}}

IDownloadProcess:定义下载接口

public interface IDownloadProcess {ResponseEntity downloadFile(String filename, String range, HttpServletRequest request, HttpServletResponse response) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException, Exception;
}

DownloadProcessImpl:下载实现类,

我们可以传range参数来处理要下载的kb数范围,当然也可以不传递就是下载全部,

1.首先就是获取桶里文件信息,文件大小什么的都能获取

2.查看是范围下载还是全部下载

3.设置响应下载的类型和请求头

4.获取minio的流文件

5.将流文件遍历读取放入缓冲中

6.然后写入到OutputStream流中,然后刷新就可以啦。

@Slf4j
@Service
public class DownloadProcessImpl implements IDownloadProcess {@Resourceprivate MinioClient minioClient;@Resourceprivate MinioConfig minioConfig;// 完整文件与分片文件下载@Overridepublic ResponseEntity downloadFile(String filename, String range, HttpServletRequest request, HttpServletResponse response) throws Exception {ResponseEntity<byte[]> responseEntity = null;BufferedOutputStream os = null;GetObjectResponse stream = null;if (StringUtils.isNotBlank(filename)) {log.info("要下载的文件:{}", filename);//String range = request.getHeader("Range");log.info("current request rang:{}", range);// 获取桶里文件信息StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filename).build());//开始下载位置long startByte = 0;//结束下载位置long endByte = statObjectResponse.size() - 1;log.info("文件开始位置:{},文件结束位置:{},文件总长度:{}", startByte, endByte, statObjectResponse.size());// 有range的话,需要根据前端下载长度进行下载,也就是分段下载// 例如:range=bytes=0-52428800if (StringUtils.isNotBlank(range) && range.contains("bytes=") && range.contains("-")) {range = range.substring(range.lastIndexOf("=") + 1).trim();String[] ranges = range.split("-");//判断range的类型if (ranges.length == 1) {//类型一:bytes=-2343if (range.startsWith("-")) endByte = Long.parseLong(ranges[0]);//类型二:bytes=2343-if (range.endsWith("-")) startByte = Long.parseLong(ranges[0]);}//类型三:bytes=22-2343else if (ranges.length == 2) {startByte = Long.parseLong(ranges[0]);endByte = Long.parseLong(ranges[1]);}}//要下载的长度long contentLength = endByte - startByte + 1;//文件类型String contentType = request.getServletContext().getMimeType(filename);//解决下载文件时文件名乱码问题byte[] fileNameBytes = filename.getBytes(StandardCharsets.UTF_8);filename = new String(fileNameBytes, 0, fileNameBytes.length, StandardCharsets.ISO_8859_1);//各种响应头设置---------------------------------------------------------------------------------------------//支持断点续传,获取部分字节内容:response.setHeader("Accept-Ranges", "bytes");//http状态码要为206:表示获取部分内容,SC_PARTIAL_CONTENT,部分浏览器不支持,所以改成SC_OKresponse.setStatus(HttpServletResponse.SC_OK);response.setContentType(contentType);response.setHeader("Last-Modified", statObjectResponse.lastModified().toString());//inline表示浏览器直接使用,attachment表示下载,fileName表示下载的文件名response.setHeader("Content-Disposition", "attachment;filename=" + filename);response.setHeader("Content-Length", String.valueOf(contentLength));//Content-Range,格式为:[要下载的开始位置]-[结束位置]/[文件总大小]response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + statObjectResponse.size());response.setHeader("ETag", "\"".concat(statObjectResponse.etag()).concat("\""));response.setContentType("application/octect-stream;charset=UTF-8");try {// 获取文件流stream = minioClient.getObject(GetObjectArgs.builder().bucket(statObjectResponse.bucket()).object(statObjectResponse.object()).offset(startByte).length(contentLength).build());os = new BufferedOutputStream(response.getOutputStream());// 将读取的文件写入到OutputStreambyte[] buffer = new byte[1024];long bytesWritten = 0;int bytesRead = -1;while ((bytesRead = stream.read(buffer)) != -1) {if (bytesWritten + bytesRead > contentLength) {os.write(buffer, 0, (int) (contentLength - bytesWritten));break;} else {os.write(buffer, 0, bytesRead);bytesWritten += bytesRead;}}os.flush();response.flushBuffer();log.info("下载完毕");// 返回对应http状态responseEntity = new ResponseEntity<byte[]>(buffer, HttpStatus.OK);} finally {if (os != null) os.close();if (stream != null) stream.close();}}return responseEntity;}
}

测试链接:

下50M的情况

http://localhost:8082/download?filename=a9500aa2091875f3d02a9b84ae1ab712.mp4&range=bytes=0-52428800

 从50m再次下载50m

http://localhost:8082/download?filename=a9500aa2091875f3d02a9b84ae1ab712.mp4&range=bytes=52428800-104857600

都下载到了前端本地以后由客户端进行合并操作就好了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/229139.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络爬虫第1天之数据解析库的使用

一、正则表达式 正则表达式&#xff08;Regular Expression 简称regex或regexp&#xff09;是一种强大的文本处理工具&#xff0c;它可以帮助实现快速的检索、替换或验证字符串中的特定模式。 1、match match()方法会尝试从字符串开始的位置到字符结束的位置匹配正则表达式&am…

gitee gihub上传步骤

上传 1. 到具体要上传的文件目录 2. 右击git Bash Here 初始化仓库&#xff1a;git init 3. 添加文件 添加所有文件 : git add . &#xff08;注意这里有个点&#xff09;添加具体文件&#xff1a; git add test.md 4. 添加到暂存区 git commit -m 暂存区 5. 将本地代…

如何将数据库导入MySQL的办法

在电脑cmd终端进行导入 首先找到MySQL中bin的位置 第一步&#xff1a;找到MySQL 第二步&#xff1a;进入MySQL 第三步&#xff1a;打开bin 第四步&#xff1a;输入cmd进入终端 第五步&#xff1a; 输入mysql -uroot -p 然后会弹出enter password&#xff1a; 输入你的密码…

C++-LD_PRELOAD

LD_PRELOAD是linux上的一个环境变量&#xff0c;作用是在程序启动前先加载这个动态链接库&#xff08;无论程序本身是否需要&#xff09;。使用场景通常是我们已经有一个release版本的动态库&#xff0c;此时我们想要调试动态库中的内容&#xff0c;又或者我们有一个v1版本的动…

Day10 Liunx高级系统设计11-数据库2

DQL:数据查询语言 查询全表 select * from 表名; 查询指定列 select 列名 1, 列名 2,… from 表名 ; 条件查询 select * from 表名 where 条件 ; 注意&#xff1a; 条件查询就是在查询时给出 WHERE 子句&#xff0c;在 WHERE 子句中可以使用如下运算符及关键 字&#…

linux笔记--VSCode利用交换机跳转服务器

目录 1--前言 2--VSCode设置 3--ssh连接 1--前言 博主学校的服务器有两个&#xff0c;其中一个服务器&#xff08;14&#xff09;可以通过挂内网VPN来进行连接&#xff0c;但另一个服务器&#xff08;15&#xff09;即使挂了VPN也不能连接&#xff0c;只能通过内网进行连接。…

条款4:确保对象在使用之前被初始化

文章目录 内置类型非局部静态对象总结 内置类型 C/C 中运行无初值的对象&#xff0c;是否确保对象初始化的规则很复杂。一般来说&#xff0c;如果使用C的C部分&#xff0c;并且初始化可能会导致运行时成本&#xff0c;则不能保证会发生。如果使用C的非C部分&#xff0c;情况有时…

深入理解前端项目中的 package.json

在前端开发中&#xff0c;package.json 是一个很重要的文件&#xff0c;它在Node.js和前端项目中扮演着重要的角色。这个文件用于存储项目的元数据以及管理项目的依赖关系。 package.json 文件是每个Node.js项目和许多前端项目的核心。它不仅定义了项目的基本属性&#xff0c;…

Docker笔记:配置docker网络与不通网络之间的通信及跨主机通信

配置 docker 网络 1 &#xff09; Docker0 网络 默认启动的所有容器&#xff0c;都会加入到这个网络中在有docker环境的 centos 中&#xff0c; 通过 $ ip addr 可以看到Docker0网络相关信息多个容器都在Docker0 网络中就属于同一个局域网了&#xff0c;就可以通信了启动三个…

ripro后台登录后转圈和图标不显示的原因及解决方法

最近&#xff0c;好多小伙伴使用ripro主题的小伙伴们都发现&#xff0c;登录后台后&#xff0c;进入主题设置就转圈&#xff0c;等待老半天后好不容易显示页面了&#xff0c;却发现图标不显示了&#xff0c;都统一显示为方框。 这是因为后台的js、css这类静态资源托管用的是js…

2312llvm,使用llvm的示例

原文 介绍 除此外,LLVM是编译器编写者的平台.因为其异常干净和小巧的IR(中间表示),使用LLVM编写编译器比其他系统容易得多. 作为证明,栈机的作者在大约四天内编写了整个编译器(语言定义,词法解析器,解析器,代码生成器等)!了解这一点很重要,因为它显示了使用LLVM时,可多快地获…

力扣刷题-二叉树-找树左下角的值

513 找树左下角的值 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1&#xff1a; 示例 2&#xff1a; 思路 层序遍历 直接层序遍历&#xff0c;因为题目说了是最底层&#xff0c;最左边的值&a…

紫光FPGA DDR3 IP使用和注意事项(axi4协议)

紫光DDR3 IP使用 对于紫光ddr3 IP核的使用需要注意事情。 阅读ddr ip手册&#xff1a; 1、注意&#xff1a;对于写地址通道&#xff0c;axi_awvalid要一直拉高&#xff0c;axi_awready才会拉高。使用的芯片型号时PG2L100H-6FBG676&#xff0c;不同的型号IP核接口和axi的握手协…

IDEA2020关于Cannot resolve symbol ‘servlet‘报错

刚开始也配置了tomcat&#xff0c;但是依然报错&#xff0c;后来查找资料解决了 在项目下面创建一个libs文件夹&#xff0c;然后将tomcat / lib文件夹中的servlet-api.jar复制了过来&#xff0c;然后再添加到library。 具体操作步骤&#xff1a;

Code automatic processing

自动化处理没啥用的代码&#xff0c;测试下&#xff0c;还不错的感觉

Elasticsearch的使用总结

Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。 put/post请求&#xff1a;http://localhost:9200/索引库名称 {"settings":{"index":{"number_of_shards":1, # 分片数量…

Axure的交互样式和情形

Axure的交互样式和情形 交互样式 Axure是一个流行的原型设计工具&#xff0c;它允许您创建交互式原型&#xff0c;模拟应用程序或网站的功能和用户界面。在Axure中&#xff0c;您可以设置各种交互样式来使原型更加生动和真实。 链接触发器&#xff1a;通过给一个元素添加链接…

风速预测(三)EMD-LSTM-Attention模型

目录 1 风速数据EMD分解与可视化 1.1 导入数据 1.2 EMD分解 2 数据集制作与预处理 2.1 先划分数据集&#xff0c;按照8&#xff1a;2划分训练集和测试集 2.2 设置滑动窗口大小为7&#xff0c;制作数据集 3 基于Pytorch的EMD-LSTM-Attention模型预测 3.1 数据加载&#…

记错vue3+ts require 报错

在main.js 中使用require 报错 ‘require’ is not defined 事先声明 &#xff0c;可能是版本不一样&#xff0c;所以解决办法不一样&#xff0c;报错的原因是Pack.json 文件中type值不同&#xff0c;解决办法有两种。 目前有什么弊端&#xff0c;因为时间比较紧&#xff0c;还…

现代C++的多线程开发

前言 早期的C进行多线程编程&#xff0c;往往需要根据不同的系统编写不同的代码&#xff0c;但是在C11之后&#xff0c;std中已经提供了多线程的支持&#xff0c;所以对于不同操作系统只需要编写一次代码即可。 本文记录一次多线程开发过程中&#xff0c;使用的C新特性&#…