前段时间闲得无聊,用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
,但是对我们的断点续传来说这个问题不大~