C++第三方库【httplib】断点续传

什么是断点续传

上图是我们平时在浏览器下载文件的场景,下载的本质是数据的传输。当出现网络异常,浏览器异常,或者文件源的服务器异常,下载都可能会终止。而当异常解除后,重新下载文件,我们希望从上一次下载的位置开始下载,而不是从头开始下载。这就是断点续传

断点续传的实现

ETag头部字段

ETag是用来标识文件的头部字段,由用户自己设定,其目的是表示文件的唯一性,修改过的文件和原文件是不同的。

ETag由服务端设置

static void download(const httplib::Request& req, httplib::Response& resp)
{
    //......

    //服务端设置ETag头部字段
    resp.set_header("ETag", "......");

    //......
}

浏览器解析响应,发现有ETag字段,保存并在下次发送GET请求时包含。ETag搭配Range字段实现断点续传

Range

当服务端返回的响应中有Accept-Ranges头部字段,代表服务端允许断点续传

客户端此时发送的请求可以携带Range头部字段,形式如下:

//服务端响应设置允许断点续传
static void download(const httplib::Request& req, httplib::Response& resp)
{
    //......

    //bytes表示客户端数据请求区间的单位
    resp.set_header("Accept-Ranges", "bytes");

    //......
}

//客户端请求断点续传区间
static void download(const httplib::Request& req, httplib::Response& resp)
{
    //......
    
    //val是服务端上一次发送的ETag
    res.set_header("If-Range", ETag);
    //val的bytes是服务端返回的断点区间的单位
    //start-end代表重传start到end区间的数据,比如5430-66758
    res.set_header("Range", "bytes=start-end");

    //......
}

//服务端返回响应
static void download(const httplib::Request& req, httplib::Response& resp)
{
    //......

    //响应的文件数据范围是start-end,文件总大小为fsize
    resp.set_header("Content-Range, "start-end/fsize");
    //重新设置ETag
    resp.set_header("ETag", "......");

    //......
}
  • 服务端设置Accept-Range字段,val值一般为bytes。该字段表示服务端支持断点续传,以字节单位传输数据
  • ETag表示服务端上某一版本资源的唯一标识,如果资源被改动,则ETag改变。客户端收到ETag表示会保存

当下载中断时,浏览器重新下载文件,则第二次的http请求需要包含If-Range字段和Range字段

  • If-Range字段:保存服务端响应的ETag字段的信息。用于服务端判断是否和上一次请求的资源一致,一致则断点续传,不一致则从头重新下载
  • Range字段:记录断点请求的数据区间,bytes start-end,表示请求服务器资源从第start字节开始到第end个字节的数据

收到客户端发送的断点续传的http响应,需要包含Content-Range字段和ETag字段4

  • Content-Range: start-end/文件大小,表示http响应包含文件数据从start开始到end结束的文件数据,文件大小表示文件总大小
  • ETag:服务端资源的唯一标识

当服务端返回的数据是断点续传的数据区间时,状态码是206

示例断点续传的请求&响应如下:

GET /download/a.txt http/1.1
If-Range: "文件唯一标识"
Range: bytes=89-999
-------------------------------------------
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Range: bytes 89-999/100000
Content-Type: application/octet-stream
ETag: "文件唯一标识"

对应文件从89到999字节的数据。

编码实现

使用httplib实现断点续传。

基本逻辑:

  1. 查看客户端请求是否包含If-Range字段,有则匹配请求文件的ETag,没有则是正常下载文件
  2. 若相同,说明客户端请求断点续传,解析Range字段,将start-end的数据填入响应的正文部分。并设置头部字段
  3. 若不同,说明客户端请求的数据被修改了,则需要从头下载,返回文件全部数据。并设置头部字段

以上逻辑,我们需要手动解析客户端响应的Range字段,但httplib已经帮我们解析了,以下源码是httplib做的部分处理

这部分表示,httplib解析客户端请求,如果包含Range字段,会自动设置响应的状态码为206

这部分是httplib解析Range字段,在返回文件数据时,会根据Range字段的解析结果,截断文件数据。所以我们在代码编写时不管是正常下载,还是断点续传都直接响应文件全部数据即可, 如果是断点续传,httplib会帮我们进行数据截断,如果start-end是5430-66347,就截出这部分数据返回给客户端

示例代码如下:

//生成ETag
//ETag = 文件名 + 文件大小 + 文件最后一次修改时间
static std::string getETag(const std::string& filename, const struct stat st)
{
    std::string ETag;
    ETag = filename + std::to_string(st.st_size) + std::to_string(st.m_tim);
    return ETag;
}    


//返回客户端要下载的文件
static void download(const httplib::Request& req, httplib::Response& resp)
{
    //1. 获取客户端请求的资源路径:req.path。截取最后一个/后的字符串,为文件名
    //2. 根据资源路径,获取文件信息
    auto pos = req.path.find_last_of('/');
    std::string filename
    if(pos == std::string::npos)
        filename = req.path;
    else
        filename = req.path.substr(pos + 1);
    struct stat st;
    stat(filename.c_str(), &st);
    //查看请求是否有If-Range(记录之前服务器响应的ETag)
    bool retrans = true;
    std::string old_etag;
    //有If-Range,两种可能:有修改,全部重传;没有修改,断点续传
    if(req.has_header("If-Range"))
    {
       old_etag = req.get_header_value("If-Range");
       if(old_etag == getETag(info, st))
           retrans = false;
    }
    //4. 读取文件信息到响应中
    std::ifstream ifs(filename.c_str(), std::ios::binary);
    //读取文件内容
    body.resize(st.s_size);
    ifs.read(&body[0], st.s_size);
    //5. 设置头部字段
    resp.set_header("ETag", getETag(info, st));
    resp.set_header("Accept-Ranges", "bytes");//提供断点续传
    resp.set_header("Content-Type", "application/octet-stream");//下载文件
    if(retrans)
    {
       //文件有修改,需要重传文件
       //ETag  Accept-Ranges bytes(断点续传)
       resp.status = 200;
    }
    else
    {
       //断点续传实现:获取头部字段中Range:bytes start-end,解析请求文件的起始和结束
       //再返回响应时,对文件内容进行截断
       //但httplib都实现了,他检测到req中有Range,然后进行处理,甚至会设置resp的状态码
       //我们只要返回完整的文件,httplib会对文件进行截断,最后响应正文中只有start-end的文件内容
                
       //resp.set_header("Content-Range", "bytes start-end/fsize");//原本还要设置这个头部字段,httplib帮我们做了
       resp.status = 206;//断点续传的状态码
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python-01

第一个程序 import randomcomputer random.randint(1, 3) print(电脑出的是:, computer) i int(input(你要出什么?1代表石头,2代表剪刀,3代表布\n)) if i computer:print(平局) elif (computer 1 and i 3) or (computer 2 …

React@16.x(20)渲染流程-首次渲染

目录 1,渲染的前置知识点1.1,React 元素1.2,React 节点1.3,节点类型1.4,真实DOM 2,首次渲染2.1,根据参数创建节点2.2,不同节点,有不同处理2.3,生成虚拟DOM树2…

LabVIEW FPGA开发NI sbRIO-9607高精度数字滤波器

使用NI sbRIO-9607硬件平台,通过LabVIEW FPGA模块实现一个高精度数字滤波器。该应用不需要额外的实时操作系统 (RT),所有控制与数据处理均在sbRIO-9607的FPGA上完成,充分利用其并行处理能力,实现低延迟、高性能的数据滤波。这种滤…

Linux系统--vi/vim编辑器

目录 vi\vim编辑器介绍 vi\vim编辑器的三种工作模式 命令模式(command mode): 输入模式 (insert mode): 底线命令模式(Last line mode): 命令的选项 查看命令帮助和…

kali配置静态ip

kali配置静态ip 因为一些环境需要,本地linux主机需要搭建一个桥接模式的网络,那么直接就在kali中配置了, 打开vim /etc/network/interfaces 这里就需要自己配置一下ip,网关,路由等内容 这里参考:参考链接 …

Django render()函数页面渲染

1, render() 函数 在Django框架中,render() 函数是一个非常有用的快捷方式,用于从视图函数返回一个完整的HTTP响应。它负责将给定的模板与上下文数据结合,渲染出最终的HTML页面,并返回一个HttpResponse对象。 from d…

[AI资讯·0605] GLM-4系列开源模型,OpenAI安全疑云,ARM推出终端计算子系统,猿辅导大模型备案……

AI资讯 1毛钱1百万token,写2遍红楼梦!国产大模型下一步还想卷什么?AI「末日」突然来临,公司同事集体变蠢!只因四大聊天机器人同时宕机OpenAI员工们开始反抗了!AI手机PC大爆发,Arm从软硬件到生态…

前端应用开发实验:组件应用

目录 实验目的相关知识点实验内容及要求代码实现效果 实验目的 (1)掌握组件的创建方法(全局组件、局部组件); (2)重点学会组件之间的数据传递(prop传值、自定义事件)&am…

单列集合--ArryList、LinkedList、Set

使用IDEA进入某个类之后,按ctrlF12,或者alt数字7,可查看该实现类的大纲。 package exercise;import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.function.Consumer;public class Demo3 {public static void…

微信小程序公众号二合一分销商城源码系统 基于PHP+MySQL组合开发的 可多商户商家入驻 带完整的安装代码包以及搭建教程

系统概述 微信小程序公众号二合一分销商城源码系统,是基于PHPMySQL组合开发的一款高效、稳定的电子商务平台解决方案。该系统创新性地将微信公众号与小程序的功能进行了深度整合,为商家提供了一个功能齐全、易于管理的分销商城系统。通过此系统&#xf…

基于聚类与统计检验深度挖掘电商用户行为

1.项目背景 在当今竞争激烈的电商市场中,了解用户的行为和需求对于制定成功的市场策略至关重要,本项目通过建立RFM模型、K-Means聚类模型,将1000个用户进行划分,针对不同类的用户,提出不同的营销策略,最后通过统计检验来探究影响用户消费行为的因素和影响用户上网行为的…

揭秘数字工厂:如何运用AGV、LMS和WMS成为制造业的隐藏神器

揭秘数字工厂:如何运用AGV、LMS和WMS成为制造业的隐藏神器 😄生命不息,写作不止 🔥 继续踏上学习之路,学之分享笔记 👊 总有一天我也能像各位大佬一样 🏆 博客首页 怒放吧德德 To记录领地 &a…

泛微开发修炼之旅--07通过后端代码实现创建并发送待办、源码及示例

文章链接:泛微开发修炼之旅--07通过后端代码实现创建并发送待办、源码及示例

C语言实战:贪吃蛇(万字详解)

💡目录 效果图 界面设计思路 1. 基本布局 2. 视觉元素 游戏机制设计 基本规则 游戏代码 前期准备 游戏代码详解 数据结构设计 宏定义 数据结构定义 函数原型(详见后文) 主函数代码 核心代码 Review 效果图 界面设计思路 1. 基…

[论文笔记]Mixtral of Experts

引言 今天带来大名鼎鼎的Mixtral of Experts的论文笔记,即Mixtral-8x7B。 作者提出了Mixtral 8x7B,一种稀疏专家混合(Sparse Mixture of Experts,SMoE)语言模型。Mixtral与Mistral 7B具有相同的架构,不同之处在于每个层由8个前馈…

Mybatis03-ResultMap及分页

1、属性名和字段名不一致问题 1.问题 数据库中的字段 新建一个项目Mybatis-04,拷贝之前,测试实体类字段不一致的情况 public class User {private int id;private String name;private String password; }select * from mybatis.user where id #{id} …

2024年会计、金融与工商管理国际会议(ICAFBA 2024)

2024年会计、金融与工商管理国际会议 2024 International Conference on Accounting, Finance, and Business Administration 【1】会议简介 2024年会计、金融与工商管理国际会议是一场集合了全球会计、金融与工商管理领域专家学者的学术盛会。此次会议旨在深入探讨会计、金融与…

【C++课程学习】:类和对象(上)(类的基础详细讲解)

🎁个人主页:我们的五年 🔍系列专栏:C课程学习 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 🍟1.1类的引出: 🍟1.2类的结构: 🍟1.3类的…

进入新公司有焦虑感怎么办?

前因 前两天技术交流群里有童鞋问了一个很有意思的问题,他问如何克服进入新公司的焦虑感?很多热心的童鞋都纷纷支招,比如 “主动干活”、“专注干活”、“让时间冲淡焦虑感”、……等等,这些都很有道理,不过&#xff…