springboot下载文件 范围下载

关键词:springboot,download,Range,Content-Range,Content-Length,http code 206 Partial Content

下载文件的一部分,我们在request header:Range 中指定要获取的文件的字节范围。要注意http response header:Content-Length 一定要与Range中所表示的获取bytes的范围长度相等。

如果Content-Length大于要Range所表示的bytes长度,那么客户端在发起请求后,下载完相应的Range范围的bytes后,连接不会自动关闭,直到连接超时,抛出异常。

如果Content-Length小于要Range所表示的bytes长度,那么发起请求后,只会获得Content-Length所指定的较小范围的bytes,导致获取的数据不完整。

controller:

    @SneakyThrows
@GetMapping("/download")
public void download(HttpServletResponse response,
@RequestParam String versionNum,
@RequestHeader(value = HttpHeaders.RANGE, required = false) String range) {
if (StringUtils.isEmpty(versionNum)) {
throw new ServiceException("版本号不能为空");
}
// jar包的上一级目录
String canonicalPath = FileUtil.getCanonicalPath(new File(".."));
String filePath = canonicalPath + "/device-upgrade-pack-repo/" + versionNum + "/" + versionNum + ".tar";
long fileSize = FileUtil.file(filePath).length(); // 获得文件流
BufferedInputStream inputStream = FileUtil.getInputStream(filePath); // offset表示从哪个字节开始读取,length表示读取多少个字节
long offset = 0;
long length;
// 如果range为空,那么就是读取整个文件
if (StringUtils.isEmpty(range)) {
length = fileSize;
} else {
// 根据range计算offset和length
Pair<Long, Long> pair = getOffset(range);
offset = pair.getLeft();
length = pair.getRight();
// length == -1 表示 end range 为最后一个字节
if (length == -1) {
length = fileSize - offset + 1;
}
// 跳过文件的offset个字节
inputStream.skip(offset);
} response.setContentType("application/octet-stream");
response.setHeader("Content-disposition",
"attachment;filename=" + versionNum + ".tar");
response.setHeader("Content-Length", String.valueOf(length));
response.setHeader("Content-Range", "bytes" + " " +
offset + "-" + (offset + length - 1) + "/" + fileSize);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
ServletOutputStream os = response.getOutputStream(); byte[] buf = new byte[1024];
int bytesRead;
// sum 是当前读取的字节数
int sum = 0;
while (sum < length && (bytesRead = inputStream.read(buf, 0, (length - sum) <= buf.length ? (int) (length - sum) : buf.length)) != -1) {
os.write(buf, 0, bytesRead);
sum += bytesRead;
} inputStream.close();
os.close();
}

解析range:

    /**
* left is offset, right is length
*
* @param range
* @return
*/
private Pair<Long, Long> getOffset(String range) {
if (!range.contains("bytes=") || !range.contains("-")) {
throw new ServiceException("range 格式错误");
}
range = range.substring(range.lastIndexOf("=") + 1).trim();
String[] split = range.split("-");
if (split.length == 0 || split.length > 2) {
throw new ServiceException("range 格式错误");
}
if (split.length == 1) {
if (range.startsWith("-")) {
return Pair.of(0L, Long.parseLong(split[0]) + 1);
} else {
// -1 表示length无限制
return Pair.of(Long.parseLong(split[0]), -1L);
}
} else {
return Pair.of(Long.parseLong(split[0]), Long.parseLong(split[1]) - Long.parseLong(split[0]) + 1);
}
}

测试下载接口:

    @Test
void downloadObject() {
OkHttpClient httpClient = HttpUtil.getHttpClient();
Request request = new Request.Builder()
.get()
.url("http://localhost:8081/device/version/download?versionNum=1.1.1.2023")
.header("Range", "bytes=0-10")
.build(); try (Response response = httpClient.newCall(request).execute()) {
InputStream inputStream = response.body().byteStream();
byte[] buf = new byte[5];
int readBytes; while ((readBytes = inputStream.read(buf)) != -1) {
if (readBytes < buf.length) {
buf = Arrays.copyOf(buf, readBytes);
}
System.out.println(Arrays.toString(buf) + " " + readBytes);
}
inputStream.close();
response.close();
System.out.println("end");
} catch (IOException e) {
throw new ServiceException("请求异常");
}
} 输出结果:
[49, 46, 49, 46, 49] 5
[46, 50, 48, 50, 51] 5
[46] 1
end

springboot下载文件 范围下载的更多相关文章

  1. 使用SpringBoot实现文件的下载

    上一篇博客:使用SpringBoot实现文件的上传 已经实现了文件的上传,所以紧接着就是下载 首先还是html页面的简单设计 <form class="form-signin" ...

  2. django 中下载文件与下载保存为excel

    一.django 中下载文件 在实际的项目中很多时候需要用到下载功能,如导excel.pdf或者文件下载,当然你可以使用web服务自己搭建可以用于下载的资源服务器,如nginx,这里我们主要介绍dja ...

  3. php 下载文件/直接下载数据内容

    思路步骤 * 定义参数 * 魔术方法 * 执行下载 * 获取设置属性函数 * 获取设置文件mime 类型 * 获取设置下载文件名 * 设置header * 下载函数 实现代码 class DownFi ...

  4. 01_基于TCP的循环为同一个客户端下载文件的下载器

    原版: TCP分为客户端(client)和服务器(server),每次服务器只能为客户端提供一次的下载服务. 改良版: TCP分为客户端(client)和服务器(server), (1)每次服务器能为 ...

  5. AFHTTPSessionManager下载文件 及下载中 进度条处理,进度条处理需要特别注意,要加载NSRunLoop 中

    1.下载文件 和进度条处理代码 - (void)timer:(NSTimer *)timer{ // 另一个View中 进度条progress属性赋值 _downloadView.progress = ...

  6. c#导出文件,下载文件,命名下载后的文件名

    Page.Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpU ...

  7. selenium 下载文件设置下载路径

    Chrome 文件下载 Chrome浏览器类似,设置其options: download.default_directory:设置下载路径 profile.default_content_settin ...

  8. UWP 下载文件显示下载进度

    <Page x:Class="WgscdProject.TestDownloadPage" xmlns="http://schemas.microsoft.com/ ...

  9. LoadRunner上传及下载文件

    (1)LoadRunner上传文件 web_submit_data("importStudent.do", "Action=https://testserver/cons ...

  10. python 通过ntlm验证下载文件

    最近使用python实现一个小工具,需要从网站下载文件,下载时服务端需要进行ntlm验证,否则返回401错误响应.经研究 requests库配合 requests-ntlm 可以解决这个问题. ntl ...

随机推荐

  1. JAVA 23种设计模式(小白进阶必经之路)

    如今几乎所有程序都遵循万物皆对象的开发理念,然在写程序中我们用的最多的应该是封装(encapsulation).继承(inheritance).多态(Polymorphism)开发模式:而更高一个境界 ...

  2. 一步步教你如何搭建K8S集群

    一.环境配置 三台CentOS7虚拟机,默认配置,内存2GB.处理器2核心. 先更新下系统 1 sudo yum update 2 sudo yum upgrade 二.安装并启动 docker 1 ...

  3. Oracle Linux切换uek内核到rhck内核解决ACFS兼容问题

    背景:协助客户做验证,客户使用的是RHEL7.6环境,我这边是OEL7.6环境,开始以为区别不大,结果acfs兼容还是遇到问题,特此记录下. 现象:asmca图形没有acfs相关内容,无法使用acfs ...

  4. 腾讯云 cloudbase 云开发使用笔记

    产品概述 云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为开发者提供高可用.自动弹性扩缩的后端云服务,包含计算.存储.托管等 serverless ...

  5. C++面试八股文:在C++中,你知道哪些运算符?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第11面: 面试官:在C++中,你都知道都哪些运算符? 二师兄:啥?运算符?+-*/=这些算吗? 面试官:嗯,还有其他的吗? 二师兄:当然还有,+=, ...

  6. FreeFileSync结合任务计划实现T级数据的全量备份和每日十几G数据的增量自动备份

    1. 背景 公司现有nas存储中有共计1.8T左右的文件数据(一般是pdf.excel.图片.压缩文件等等格式),因为nas无法做备份:担心后面nas出现故障造成数据丢失,现急需一个解决方案实现如下目 ...

  7. 什么是hive的高级分组聚合,它的用法和注意事项以及性能分析

    hive的高级分组聚合是指在聚合时使用GROUPING SETS.CUBE和ROLLUP的分组聚合. 高级分组聚合在很多数据库类SQL中都有出现,并非hive独有,这里只说明hive中的情况. 使用高 ...

  8. 统信UOS系统开发笔记(八):在统信UOS上编译搭建mqtt基础环境(版本使用QMQTT::Clinet)

    前言   统信uos使用到mqtt开发,需要重新编译mqtt,本篇描述统信uos20上的mqtt源码编译和环境搭建.   注意   这里下载的mqtt版本与其他几篇文章的不同,这里是使用QMQTT:: ...

  9. 【Python】数据可视化利器PyCharts在测试工作中的应用

    PyCharts 简介 PyCharts 是一个基于 Python 的数据可视化库,它支持多种图表类型,如折线图.柱状图.饼图等.PyCharts 提供了简洁的 API,使得用户能够轻松地创建各种图表 ...

  10. Java架构师之路:从Java码农到年薪八十万,最牛Java架构师进阶路线

    Java架构师之路:从Java码农到年薪八十万,最牛Java架构师进阶路线 摘要:本文将为Java开发工程师提供一条从Java码农到年薪八十万的进阶之路,探讨如何成为一名顶尖的Java架构师.我们将介 ...