Flask动态生成Zip文件后无法触发下载问题求助
Flask动态生成Zip后无法触发下载的问题解决
问题描述
我尝试动态生成Zip文件,发送给用户后从服务器删除,目前编写了如下代码:
@app.route('/download', methods=['GET']) def download(): shutil.make_archive('files', 'zip', dir_path+'/FILES') while not os.path.exists(dir_path+"/files.zip"): time.sleep(1) file_path = dir_path + "/files.zip" cache = tempfile.NamedTemporaryFile() with open(file_path, 'rb') as fp: shutil.copyfileobj(fp, cache) cache.flush() cache.seek(0) os.remove(file_path) return send_file(cache, as_attachment=True, mimetype='application/zip', download_name='data.zip')
所有操作均显示正常,响应状态码为200,但始终无法触发文件下载提示。当前使用Flask内置开发服务器,请问是否遗漏了某些配置或细节?
问题原因
- 临时文件生命周期问题:默认的
tempfile.NamedTemporaryFile()会在文件对象被关闭后自动删除。Flask的send_file是异步输出内容的,视图函数返回后,cache对象会被垃圾回收机制关闭,导致文件内容已不存在,浏览器无法获取完整数据,因此不会触发下载。 - 多余的等待逻辑:
shutil.make_archive是同步执行的函数,执行完成后Zip文件已经生成,你写的while循环完全没必要。
修复方案
方案一:修正临时文件处理逻辑
创建临时文件时设置delete=False避免自动删除,发送完成后手动清理临时文件:
import os import shutil import tempfile from flask import send_file @app.route('/download', methods=['GET']) def download(): # 直接指定Zip输出路径,同步生成文件 zip_path = os.path.join(dir_path, 'files.zip') shutil.make_archive(os.path.splitext(zip_path)[0], 'zip', os.path.join(dir_path, 'FILES')) # 创建不自动删除的临时文件 with tempfile.NamedTemporaryFile(delete=False) as cache: with open(zip_path, 'rb') as fp: shutil.copyfileobj(fp, cache) # 删除服务器上的原始Zip文件 os.remove(zip_path) # 发送文件并确保临时文件被清理 try: return send_file( cache.name, as_attachment=True, mimetype='application/zip', download_name='data.zip' ) finally: os.unlink(cache.name)
方案二:内存中生成Zip(更高效)
直接在内存中构建Zip文件,完全避免服务器上的物理文件操作:
import io import os import zipfile from flask import send_file @app.route('/download', methods=['GET']) def download(): memory_file = io.BytesIO() # 在内存中创建Zip文件 with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf: files_dir = os.path.join(dir_path, 'FILES') # 遍历目标目录,添加所有文件到Zip for root, _, files in os.walk(files_dir): for file in files: file_path = os.path.join(root, file) # 用相对路径作为Zip内的文件名,避免暴露服务器路径 arcname = os.path.relpath(file_path, files_dir) zf.write(file_path, arcname) # 重置文件指针到开头 memory_file.seek(0) return send_file( memory_file, as_attachment=True, mimetype='application/zip', download_name='data.zip' )
内容的提问来源于stack exchange,提问作者pl0x
相关产品推荐
相关产品推荐

