1 是什么

文件在传输中断后,下次传输可以在上次中断的位置继续传输未传输的那部分。

2 有什么用

如果碰到网络故障,或者暂停,可以从已经上传或下载的部分开始继续上传或下载未完成的部分,而没有必要从头开始上传下载,用户可以节省时间,提高速度。

3 怎么做

3.1 HTTP请求头Range

range是请求资源的部分内容(不包括响应头的大小),单位是byte,即字节,从0开始。
如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码及说明。
如果不能处理这种Range的话,就会返回整个资源以及响应状态码为 200 OK。

3.2 Range请求头格式

Range: bytes=start-end

ange头域可以请求实体的一个或者多个子范围。例如,
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999
例如

Range: bytes=10- :第10个字节及最后个字节的数据
Range: bytes=40-100 :第40个字节到第100个字节之间的数据.

注意,这个表示[start,end],即是包含请求头的start及end字节的,所以,下一个请求,应该是上一个请求的[end+1, nextEnd]

4 例子

可以发送一个请求头有"Range":"bytes=0-"的head请求,测试一下服务求是否支持断点续传,如果返回状态码是206,并且返回头部header中有Range-content等标志,那就是支持断点续传。

# 同步下载文件加断点续传
import requests
import time
import os


def download(url, filename):
    start = time.time()
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0"
    }
    headers["Range"] = "bytes=0-"
    resp = requests.head(url=url, headers=headers)
    if resp.status_code == 206:
        print('服务器支持断点续传...')
        file_size = int(resp.headers["content-length"])
        if os.path.exists(filename):
            wrote_byte = os.path.getsize(filename)
            print('继续下载文件...')
        else:
            wrote_byte = 0
            print('开始下载文件...')
        if wrote_byte >= file_size:
            return
        headers["Range"] = f"bytes={wrote_byte}-"
        rep = requests.get(url=url, headers=headers, stream=True)
        with open(filename, "ab") as f:
            for chunk in rep.iter_content(chunk_size=1024):  # (6)
                if chunk:
                    f.write(chunk)
                    f.flush()
        end = time.time() - start
        print(f"用了{end}秒,或{end/60}分钟")
    else:
        print('服务器不支持断点续传......')