如何在Django中流式传输整个文件夹且不读入全部内容到内存
Django 实现流式文件夹下载(无需全量加载内存)
针对现有方案的问题:
- 需将所有文件加载进内存,大文件场景易引发内存溢出(OOM)
- 迭代器示例无法配合
open(file, 'rb').read(chunk_size)实现分块读取 - 仅实现多文件分组,未保留原文件夹结构打包
以下是真正的流式文件夹下载实现,核心思路是通过分块读写生成ZIP压缩包,同时用Django的StreamingHttpResponse逐块返回内容,完全避免全量加载内存:
实现代码
import os import zipfile from io import BytesIO from django.http import StreamingHttpResponse def stream_zip(folder_path): zip_buffer = BytesIO() # 使用压缩格式,追求速度可替换为zipfile.ZIP_STORED with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file: # 遍历目标文件夹的所有层级 for root, _, files in os.walk(folder_path): for file_name in files: file_full_path = os.path.join(root, file_name) # 保留原文件夹结构,作为压缩包内的文件路径 arcname = os.path.relpath(file_full_path, folder_path) # 分块读取本地文件,写入压缩包 with zip_file.open(arcname, 'w') as zip_entry: with open(file_full_path, 'rb') as local_file: # 8KB块大小,可根据需求调整 while chunk := local_file.read(8192): zip_entry.write(chunk) # 输出当前已生成的压缩包内容块 zip_buffer.seek(0) yield zip_buffer.read() # 重置缓冲区,准备下一个文件的写入 zip_buffer.seek(0) zip_buffer.truncate() # 输出压缩包收尾内容 zip_buffer.seek(0) yield zip_buffer.read() def download_folder_view(request): # 替换为你的目标文件夹绝对路径 target_folder = "/path/to/your/folder" response = StreamingHttpResponse( stream_zip(target_folder), content_type="application/zip" ) response["Content-Disposition"] = 'attachment; filename="downloaded_folder.zip"' return response
方案优势
- 低内存占用:每个文件分块读取、分块写入压缩包,再分块返回给客户端,内存仅需容纳单个文件块和少量压缩缓存
- 真实文件夹打包:通过
os.path.relpath保留原文件夹的层级结构,解压后与原文件夹一致 - 兼容分块读取:直接使用
open(file, 'rb').read(chunk_size)实现文件分块,完全匹配需求
注意事项
- 确保Django运行进程拥有目标文件夹及所有文件的读取权限
- 块大小(示例中为8KB)可根据服务器性能、文件平均大小调整,平衡内存占用和传输效率
- 若无需压缩,将
zipfile.ZIP_DEFLATED替换为zipfile.ZIP_STORED可大幅提升打包速度
内容的提问来源于stack exchange,提问作者Natthaphon Hongcharoen
相关产品推荐
相关产品推荐

