如何使用Sanic流式生成并下载超大Zip文件?
解决Sanic流式生成大压缩包中断问题
修复流式响应的发送逻辑
你的代码中断的核心原因很可能是没有正确标记响应块的结束状态,导致客户端一直在等待后续数据,最终超时断开。Sanic的response.send()方法默认会把more_body设为False,意味着当前是最后一块数据;但你循环发送所有chunk时都用了默认值,会让客户端提前认为传输结束,或者后续等待超时。
正确的做法是给除了最后一块之外的所有chunk标记more_body=True,最后一块不标记(或设为False):
import os from zipstream import ZipStream async def download_zip(request, dataset_id, file_path): zs = ZipStream(chunk_size=1024*1024) # 调整为1MB块大小,减少发送次数 for root, _, files in os.walk(file_path): for file in files: fpath = str(os.path.join(root, file)) zs.add_path(fpath) response = await request.respond( headers={'Content-Disposition': f'attachment; filename="{dataset_id}.zip"'}, content_type='application/zip' ) chunk_iter = iter(zs) # 处理第一个chunk prev_chunk = next(chunk_iter, None) if not prev_chunk: await response.send(b'') return # 发送中间chunk,标记more_body=True for chunk in chunk_iter: await response.send(prev_chunk, more_body=True) prev_chunk = chunk # 发送最后一个chunk,不标记more_body(默认False) await response.send(prev_chunk)
调整Sanic超时配置
传输10GB大文件需要更长的连接时间,Sanic默认的超时设置可能会主动断开连接。在你的Sanic应用配置中调大相关超时参数:
app = Sanic("MyApp") # 设置1小时超时(根据实际需求调整) app.config.RESPONSE_TIMEOUT = 3600 app.config.REQUEST_TIMEOUT = 3600
关于模拟文件适配file_stream的方案
file_stream()本质是基于真实文件路径实现的异步流式传输,无法直接适配ZipStream的动态生成场景。如果一定要复用类似逻辑,你可以自定义一个异步类文件对象,将ZipStream的chunk输出封装为read()方法,但这种方式并没有比修复流式响应更简单,反而增加复杂度,不推荐。
内容的提问来源于stack exchange,提问作者Cristian M
相关产品推荐
相关产品推荐

