首页 > 编程语言 >Tips:Java 文件断点下载

Tips:Java 文件断点下载

时间:2024-12-12 19:27:37浏览次数:5  
标签:Java 字节 bytes range Content Range HTTP Tips 断点

前言

互联网的连接速度慢且不稳定,有可能由于网络故障导致断开连接。

在客户端下载一个大对象时,因网络断开导致上传下载失败的概率就会变得不可忽视。

图片

客户端在GET对象请求时通过设置Range头部来告诉接口服务需要从什么位置开始输出对象的数据。

判断是否支持断点下载,根据文档:14.35.1 Byte Rangeshttps://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

// 直接判断是否有 Accept-Ranges = bytes
boolean support = urlConnection.getHeaderField("Accept-Ranges").equals("bytes");
System.out.println("Partial content retrieval support = " + (support ? "Yes" : "No));

例如:

donald@donald-pro:~$ curl -i --range 0-9 http://localhost:8080/file/chunk/download
HTTP/1.1 206
Accept-Ranges: bytes
Content-Disposition: inline;filename=pom.xml
Content-Range: bytes 0-9/13485
Content-Length: 10
Date: Mon, 01 Nov 2021 09:53:25 GMT

直接判断头部 HEAD,例如:

HeadObject 接口用于获取某个文件(Object)的元信息。使用此接口不会返回文件内容。

HEAD /ObjectName HTTP/1.1
Host: BucketName.oss-cn-hangzhou.aliyuncs.com
Date: GMT Date
Authorization: SignatureValue

需知,对应 HTTP 状态码:

  • 206 Partial ContentHTTP Range 请求成功

  • 416 Requested Range Not Satisfiable statusHTTP Range 请求超出界限

  • 200 OK:不支持范围请求

小结如下:

  1. HTTP 范围请求:需要 HTTP/1.1 及之上支持,如果双端某一段低于此版本,则认为不支持。

  2. 通过响应头中的 Accept-Ranges 来确定是否支持范围请求。

  3. 通过在请求头中添加 Range 这个请求头,来指定请求的内容实体的字节范围。

  4. 在响应头中,通过 Content-Range 来标识当前返回的内容实体范围,并使用 Content-Length 来标识当前返回的内容实体范围长度。

  5. 在请求过程中,可以通过 If-Range 来区分资源文件是否变动,它的值来自 ETag 或者 Last-Modifled。如果资源文件有改动,会重新走下载流程。

生产实战

开发也得依靠依据,设定好边界,才能掌控全局。

有现成的文档,来看阿里云文档https://help.aliyun.com/document_detail/39571.html

  • Range: bytes=0-499:表示第0~499字节范围的内容。

  • Range: bytes=500-999:表示第500~999字节范围的内容。

  • Range: bytes=-500:表示最后500字节的内容。

  • Range: bytes=500-:表示从第500字节开始到文件结束部分的内容。

  • Range: bytes=0-:表示第一个字节到最后一个字节,即完整的文件内容。微信搜索公众号:Java后端编程,回复:java 领取资料 。

HTTP Range 是否合法对应处理:

  • 如果 HTTP Range 请求合法,响应返回值为 206 并在响应头中包含 Content-Range

  • 如果 HTTP Range 请求不合法,或者指定范围不在有效区间,会导致 Range 不生效,响应返回值为200,并传送整个 Object 内容。

如下为 HTTP Range 请求不合法的示例及错误说明: 假设 Object 资源大小为1000字节,Range 有效区间为0~999

  • Range: byte=0-499:格式错误,byte 应为 bytes

  • Range: bytes=0-1000:末字节 1000 超出有效区间。

  • Range: bytes=1000-2000:指定范围超出有效区间。

  • Range: bytes=1000-:首字节超出有效区间。

  • Range: bytes=-2000:指定范围超出有效区间。

举一些栗子:

# 正常范围下载
donald@donald-pro:~$ curl -i --range 0-9 http://localhost:8080/file/chunk/download
HTTP/1.1 206 
Accept-Ranges: bytes
Content-Disposition: inline;filename=Screen_Recording_20211101-162729_Settings.mp4
Content-Range: bytes 0-9
Content-Type: application/force-download;charset=UTF-8
Content-Length: 16241985
Date: Wed, 03 Nov 2021 09:50:50 GMT

服务端 - 业务开发

这里以 SpringBoot 为栗子:

  1. 对外支持 range 下载

  2. 底层存储:使用 ceph

  3. Controller 如下:

@Slf4j
@RestController
public class Controller {
    @Autowired
    private FileService fileService;
    
    /**
     * 下载文件
     *
     * 对外提供
     *
     * @param fileId 文件Id
     * @param token token
     * @param accountId 帐号Id
     * @param response 响应
     */
    @GetMapping("/oceanfile/download")
    public void downloadOceanfile(@RequestParam String fileId,
                                  @RequestHeader(value = "Range") String range,
                                  HttpServletResponse response) {

        this.fileService.downloadFile(fileId, response, range);
    }
}
  1. Service 如下:

@Slf4j
@Service
public class FileService {
    @Autowired
    private CephUtils cephUtils;
    
    /**
     * 直接下载文件
     *
     * Tips: 支持断点下载
     * @param fileId 文件Id
     * @param response 返回
     * @param range 范围
     */
    public void downloadFile(String fileId, HttpServletResponse response, String range) {
        // 根据 fileId 获取文件信息
        FileInfo fileInfo = getFileInfo(fileId);

        String bucketName = fileInfo.getBucketName();
        String relativePath = fileInfo.getRelativePath();

        // 处理 range,范围信息
        RangeDTO rangeInfo = executeRangeInfo(range, fileInfo.getFileSize());

        // rangeInfo = null,直接下载整个文件
        if (Objects.isNull(rangeInfo)) {
        
            cephUtils.downloadFile(response, bucketName, relativePath);
            return;
        }
        // 下载部分文件
        cephUtils.downloadFileWithRange(response, bucketName, relativePath, rangeInfo);
    }

    private RangeDTO executeRangeInfo(String range, Long fileSize) {

        if (StringUtils.isEmpty(range) || !range.contains("bytes=") || !range.contains("-")) {

            return null;
        }

        long startByte = 0;
        long endByte = fileSize - 1;

        range = range.substring(range.lastIndexOf("=") + 1).trim();
        String[] ranges = range.split("-");

        if (ranges.length <= 0 || ranges.length > 2) {

            return null;
        }

        try {
            if (ranges.length == 1) {
                if (range.startsWith("-")) {

                    //1. 如:bytes=-1024  从开始字节到第1024个字节的数据
                    endByte = Long.parseLong(ranges[0]);
                } else if (range.endsWith("-")) {

                    //2. 如:bytes=1024-  第1024个字节到最后字节的数据
                    startByte = Long.parseLong(ranges[0]);
                }
            } else {
                //3. 如:bytes=1024-2048  第1024个字节到2048个字节的数据
                startByte = Long.parseLong(ranges[0]);
                endByte = Long.parseLong(ranges[1]);
            }
        } catch (NumberFormatException e) {
            startByte = 0;
            endByte = fileSize - 1;
        }
        
        if (startByte >= fileSize) {
            
            log.error("range error, startByte >= fileSize. " +
                    "startByte: {}, fileSize: {}", startByte, fileSize);
            return null;
        }

        return new RangeDTO(startByte, endByte);
    }
}

标签:Java,字节,bytes,range,Content,Range,HTTP,Tips,断点
From: https://blog.csdn.net/java_121388/article/details/144434256

相关文章

  • 一周掌握 Java 入门知识
    学习目标:提示:这里可以添加学习目标例如:一周掌握Java入门知识学习内容:提示:这里可以添加要学的内容例如:搭建Java开发环境掌握Java基本语法掌握条件语句掌握循环语句学习时间:提示:这里可以添加计划学习的时间例如:周一至周五晚上7点—晚上9点周六上午9点......
  • Java学习笔记-IO流
    Java学习笔记-IO流文章目录Java学习笔记-IO流一、IO流的概述1.1IO流体系1.2字节流FileOutputStreamFileInputStream文件拷贝的代码IO流中不同jdk版本捕获异常的方式1.3字符集ASCII英文GBK英文中文Unicode英文中文1.4java中编码和解码1.5字符输入流和输出流Fil......
  • Elasticsearch Java Api Client中DSL语句的查询方法汇总
    说明:示例代码依赖的是co.elastic.clients:elasticsearch-java:8.16.1。1、termQuery方法用途:用于精确匹配某个字段的完全相等的值。这在查询如文档的ID、状态码等具有明确取值的字段时非常有用。参数说明:field:这是一个字符串参数,用于指定要进行精确匹配查询的字段名称......
  • JAVA基本语法(三)
    教程目录JAVA基础教程JAVA基本语法一、程序流程控制1.1、顺序结构1.2、if-else结构1.3、switch-case结构1.3、循环结构1.3.1、for循环1.3.2、while循环1.3.3、do-while循环1.3.4、break的使用1.3.5、return的使用JAVA基础教程JAVA基本语法一、程序流程控制......
  • javaEE毕业设计基于ssm框架的在线考试系统的设计与实现jau62
    目录项目介绍具体实现截图开发核心技术:核心代码部分展示详细视频演示源码获取方式项目介绍在各学校的教学过程中,用户的考试是一项非常重要的事情。随着计算机多媒体技术的发展和网络的普及,“基于网络的学习模式”正悄无声息的改变着传统的教室学习模式,“在线考试系......
  • javaEE毕业设计基于ssm的在线教学视频播放网站的设计与实现-r0xl4
    目录项目介绍具体实现截图开发核心技术:核心代码部分展示详细视频演示源码获取方式项目介绍在线视频网站的目的是实现满足在线视频播放、视频上传和下载等影视剧迷们对影视剧的日常需求的功能。为了达到这个目的,于是对系统提出了以下的系统功能:(1)能够在线播放视频,实......
  • 基于java中的SSM框架实现龙腾公司员工信息管理系统项目【附项目源码+论文说明】
    基于java中的SSM框架实现龙腾公司员工信息管理系统演示【内附项目源码+LW说明】摘要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本龙腾公司员工信息管理系统就是在这样的大环境下诞生,其可以帮助......
  • 基于java中的SSM框架实现校园快递代取系统项目【附项目源码+论文说明】
    基于java中的SSM框架实现校园快递代取系统演示【内附项目源码+LW说明】摘要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本校园快递代取系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内......
  • 基于Java中的SSM框架实现党务政务服务热线平台项目【项目源码+论文说明】
    摘要首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要包罗软件架构模式、整体功能模块、数据库设计......
  • 基于Java中的SSM框架实现电子资源管理系统项目【项目源码+论文说明】
    摘要随着互联网技术的高速发展,人们生活的各方面都受到互联网技术的影响。现在人们可以通过互联网技术就能实现不出家门就可以通过网络进行系统管理,交易等,而且过程简单、快捷。同样的,在人们的工作生活中,也就需要互联网技术来方便人们的日常工作生活,实现工作办公的自动化处理,实......