土豆不好吃

给 Sanic 应用添加断点续传支持

前段时间闲得无聊,用sanic写了个临时文件网盘,名字比较奇怪就叫“新疆大盘鸡”。长这样

支持打开网页上传文件,也支持命令行上传。

后来在吃狗粮时发现两个问题,一个是下载大文件,不知道文件有多大,因此浏览器一直下载无法渲然进度条。这个很好解决,直接加上content length就可以

headers = {"content-length": str(path.stat().st_size)}
return await file_stream(path, headers=headers)

第二个问题则是发现无法断点续传。如果因为网络问题闪断了,那就又要从头开始了,会很苦恼;同时也不方便stream

断点续传请求起来是像下面这样子的:

利用了HTTP标准的range header,然后指定起点、终点,需要注意这里可能有多种格式,比如省略,多个范围。

实现起来其实不难,就是解析header,然后读文件时根据范围定位到对应的部分,只返回这部分文件内容。问题是自己写起来比较痛苦😂

非常幸运的是,sanic自带了一个简单的函数可以让我们迅速的支持Range

from sanic.handlers import ContentRangeHandler
@app.get("/")
async def hello_world(request: Request):
    return await file_stream("README.md", _range=ContentRangeHandler(request, stats=os.stat("README.md")))

很简单是不是。

要注意的是如果 Range 请求头不存在,会抛出 HeaderNotFound 的异常,因此有必要try catch一下,比如

try:
    return await file_stream("README.md", _range=ContentRangeHandler(request, stats=os.stat("README.md")))
except HeaderNotFound:
    return await file_stream("README.md")
except Exception as e:
    return text(str(e))

非常不幸的是,这个 ContentRangeHandler 不支持多重范围,比如说bytes=10-100,300-400,但是对我们的断点续传来说这个问题不大~


文章版权归原作者所有丨本站默认采用CC-BY-NC-SA 4.0协议进行授权|
转载必须包含本声明,并以超链接形式注明原作者和本文原始地址:
https://dmesg.app/sanic-range.html
退出移动版