zoukankan      html  css  js  c++  java
  • HTTP文件断点续传的原理

    前几天一个同事跑过来找我说,我们在广告素材视频这块想做断点续传,就是这次某个视频缓存到一半,下次不用重头开始,可以在原来停留得位置开始继续下载.以提供更好的用户体验。

    同时说需要我们支持吐素材地址的业务接口告诉终端最后修改时间/文件签名(md5),用这个用来判断我当前要下的文件有没有变化,同时告诉终端文件的Size大小.

    我一细想,这个问题压根不需要通过改变现有接口提供更多的数据来做.下面从原理实现上简单说下:

    关键点:

    对于断点续传,关键点是两个:

    1. 终端知道当前的文件和上一次加载的文件是不是内容发生了变化,如果有变化,需要重新从offset 0 的位置开始下载

    2. 终端记录好上次成功下载到的offset,告诉server端,server端支持从特定的offset 开始吐数据

    文件变化感知:

    前置业务接口方案:

    对于关键点1,对于决定大部分产品的业务场景,可以通过前置业务接口解决;这里简单介绍一下:

    对于非下载工具类的产品,如视频APP(奇艺,优酷),视频播放前会请求相关业务的信息,主要返回片子叫什么名字,主要演员等等一些列信息,同时会返回一个对于播放最重要的信息——播放地址。 

    播放地址就是我们可以做文章的地方,如果《太子妃第一集》这个片子更新了(被广电要求减掉某个污的画面),可以后端系统让这个业务接口吐不同的播放地址/一个不同的url参数(?ver=1.1)/位置参数(#ver1.1)。这样纯天然的URL变化能纯天然的让终端认为不是同一个片子,而需要重新加载。

    HTPP 标准ETAG方案:

    没有业务接口的下载工具类的如何解决呢?

    下载工具类的没有前置接口,可以使用HTTP 的ETAG来标识是否文件已经修改。

    ETAG原理:如果URL上的资源内容改变,一个新的不一样的ETag就会被分配。用这种方法使用ETag即类似于指纹,并且他们能够被快速地被比较,以确定两个版本的资源是否相同。ETag的比较只对同一个URL有意义——不同URL上的资源的ETag值可能相同也可能不同,从他们的ETag的比较中无从推断。

    ETAG是HTTP的一个可选字段,且没有规范他的实现;实际上业内用的比较多的就是使用MD5签名的方式来生成(linux shell md5sum)

    典型用法:

    server端: Nginx >1.3.3 自带有ETAG的module , 当然同时也可以在业务代码里SetHeaders加一个ETAG字段

    client端:

    第一次请求时:

    String etag = httpURLConnection.getHeaderField("ETag");

    ETag: "b428eab9654aa7c87091e"

    第二次请求(断点续传时):

    httpURLConnection.setRequestProperty(“If-None-Match”, "b428eab9654aa7c87091e"); 

    If-None-Match: "b428eab9654aa7c87091e"

    如果ETag值匹配,这就意味着资源没有改变,服务器便会发送回一个极短的响应,包含HTTP “304 未修改”的状态。304状态告诉客户端,它的缓存版本是最新的,并应该使用它。

    然而,如果ETag的值不匹配,这就意味着资源很可能发生了变化,那么,一个完整的响应就会被返回,包括资源的内容,就好像ETag没有被使用。这种情况下,客户端可以用新返回的资源和新的ETag替代先前的缓存版本。

    续传支持:

    对于一个C/C++程序员,第一时间会得出一个系统级实现方案:

    1. 客户端传当前的offset

    2. server端seek到文件特定的offset开始读取往http connection吐数据

    不过我们深处在一个开放方案和标准不断完善的时代,不需要自己实现一个(这也是像我这样的C/C++研发工程师越来越没落的原因),来看看HTTP协议是怎么解决这个问题的:

    HTTP头Range字段:

    Range : 用于客户端到服务器端的请求,可通过该字段指定下载文件的某一段大小,及其单位。典型的格式如: 

    Range: bytes=0-499 下载第0-499字节范围的内容
    Range: bytes=500-999 下载第500-999字节范围的内容
    Range: bytes=-500 下载最后500字节的内容
    Range: bytes=500- 下载从第500字节开始到文件结束部分的内容

    来个简单粗暴的例子

    curl --header "Range: bytes=0-20000" xxx.com/memcache.pdf -o part1
    curl --header "Range: bytes=20001-223651" xxx.com/memcache.pdf -o part2
    cat part1 part2 >> a.pdf

    衍生阅读:

     使用ETags减少Web应用带宽和负载

  • 相关阅读:
    总结的git操作命令小抄集
    两种方式实现压缩文件或文件夹
    eclipse 中执行 main 函数如何添加参数
    alert()、confirm()和prompt()的区别与用法
    阿里某安全工程师写的明星代码
    在 Linux 环境下报错 java.lang.reflect.InvocationTargetException
    MyBatis学习-SQL 符号篇
    初识IP基础分类、CIDR
    Snort
    Dshell----开源攻击分析框架
  • 原文地址:https://www.cnblogs.com/Creator/p/5490929.html
Copyright © 2011-2022 走看看