Django如何流式下载远程文件打包ZIP无全量内存加载返回前端
结论
Python 搭配 Django 完全可以实现你要的无全量内存占用的流式ZIP打包返回逻辑,你当前的代码没有达到预期,是核心用法存在几个明显错误,完全没触发真正的流式流转。
现有代码的核心问题
- 引入了
io.BytesIO()作为ZIP写入缓冲区,最后还调用zip_buffer.getvalue()读取全部内容,这一步会把整个ZIP包完整加载到内存,直接违背了低内存占用的设计目标。 - 对远程请求虽然开了
stream=True,但直接读取r.content属性,这个属性会自动把远程文件的全部内容拉取到内存,根本没有用到分块流式读取的能力。 zipstream的write_iter方法需要接收分块产出内容的可迭代对象,你传入的是已经加载完成的完整字节串r.content,完全没有触发迭代写入的逻辑。- 代码逻辑是等ZIP全部写入BytesIO之后,才把完整内容传给
StreamingHttpResponse,这时候所有内容都已经驻留内存,根本做不到边生成边返回给前端。
正确实现代码
首先确保安装了依赖:pip install django requests zipstream
视图代码参考如下:
import zipstream import requests from django.http import StreamingHttpResponse def stream_download_zip(request): # 配置项替换成你自己的参数 inner_file_name = "zy.bin" remote_url = "https://download-file/zy" chunk_size = 1024 * 1024 # 每次读写1MB块,可根据服务器配置调整 # 初始化ZipFile对象,不需要绑定BytesIO缓冲区,它本身就是逐块产出内容的可迭代对象 zf = zipstream.ZipFile( mode="w", compression=zipstream.ZIP_DEFLATED, allowZip64=True # 开启支持大于4G的大文件打包 ) # 远程文件分块读取生成器,每次只读一个块,不会加载全量文件到内存 def remote_file_iter(): with requests.get(remote_url, stream=True, timeout=60) as resp: resp.raise_for_status() for chunk in resp.iter_content(chunk_size=chunk_size): if chunk: # 过滤连接保活产生的空块 yield chunk # 往ZIP流中写入流式文件,传入迭代器而非完整内容 zf.write_iter(inner_file_name, remote_file_iter()) # 直接将ZipFile可迭代对象传给StreamingHttpResponse,逐块生成逐块返回给前端 response = StreamingHttpResponse(zf, content_type="application/zip") response["Content-Disposition"] = 'attachment; filename="zip-files.zip"' return response
关键注意事项
- 不要在返回响应前主动遍历、读取ZipFile实例的全部内容,否则会导致全量内容加载到内存,直接把实例传给
StreamingHttpResponse即可,框架会自动逐块迭代读取。 - 如果需要打包多个远程文件,只需要重复调用
zf.write_iter(),给每个文件配置对应的分块读取生成器即可,全程内存中只会驻留当前处理的单个数据块,不会缓存完整文件或完整ZIP包。 - 分块大小建议设置在1MB~10MB区间,过小会增加IO次数,过大会拉高单请求内存占用。
内容的提问来源于stack exchange,提问作者Raheela Aslam
相关产品推荐
相关产品推荐

