Flask后端返回send_file时,如何向前端发送任务进度数据?
实现Flask下载进度实时更新的简单方案
你原来的代码是一次性返回压缩包,没法中途给前端发消息,得改成「后台干活+前端定期查进度」的模式,下面是具体步骤:
一、后端代码修改
1. 导入必要模块
先把需要的工具包导入进来:
from flask import Flask, request, redirect, url_for, render_template, session, jsonify, send_file from zipfile import ZipFile import threading import os from pytube import YouTube
2. 改写接口逻辑
把下载打包的任务放到后台线程,同时新增一个接口让前端查进度:
@app.route('/main', methods=['GET', 'POST']) def main(): if request.method == 'POST': if not current_user: return redirect(url_for('home')) # 初始化进度:当前完成数、总文件数、是否完成 session['download_progress'] = {'current': 0, 'total': 10, 'done': False} # 启动后台线程处理下载打包 thread = threading.Thread(target=process_files, args=(current_user.id,)) thread.daemon = True # 后台线程随主服务关闭自动结束 thread.start() # 跳转到进度展示页 return render_template('progress.html', user=current_user) return render_template('main.html', user=current_user) # 后台处理下载打包的函数 def process_files(user_id): zip_temp_path = f'temp_{user_id}.zip' total_files = 10 # 你需要提前确定总共有多少个要下载的文件 session['download_progress']['total'] = total_files with ZipFile(zip_temp_path, "w") as zip_file: for counter in range(1, total_files+1): # 这里写你的pytube下载逻辑 # 示例:yt = YouTube("目标视频链接"); stream = yt.streams.get_highest_resolution(); stream.download() # 把下载好的文件加入压缩包:zip_file.write(下载的文件名) # 更新当前进度到会话 session['download_progress']['current'] = counter # 标记任务完成 session['download_progress']['done'] = True # 前端用来查询进度的接口 @app.route('/get-progress') def get_progress(): progress = session.get('download_progress', {'current': 0, 'total': 0, 'done': False}) return jsonify(progress) # 提供压缩包下载的接口 @app.route('/download-zip') def download_zip(): if not current_user: return redirect(url_for('home')) zip_temp_path = f'temp_{current_user.id}.zip' if os.path.exists(zip_temp_path): response = send_file(zip_temp_path, download_name="something.zip", as_attachment=True) # 下载完成后删除临时文件 os.remove(zip_temp_path) # 清空会话里的进度数据 session.pop('download_progress', None) return response return "文件尚未准备好,请稍后重试"
二、前端页面修改
1. 进度展示页(progress.html)
这个页面负责显示实时进度,完成后自动触发下载:
<!DOCTYPE html> <html> <head> <title>处理中...</title> </head> <body> <h3>正在为您打包文件</h3> <div id="progress-info">0/0个文件已完成</div> <script> // 每隔1秒查询一次进度 const progressChecker = setInterval(() => { fetch('/get-progress') .then(res => res.json()) .then(data => { // 更新页面上的进度文本 document.getElementById('progress-info').textContent = `${data.current}/${data.total}个文件已完成`; // 如果任务完成,停止查询并跳转下载 if (data.done) { clearInterval(progressChecker); window.location.href = '/download-zip'; } }); }, 1000); </script> </body> </html>
2. 主页面(main.html)
保持原有提交按钮即可,点击后会触发后台任务并跳转到进度页。
三、核心逻辑说明
- 后台线程:把下载打包的耗时操作放到后台,避免前端页面长时间卡住。
- 会话存进度:每个用户的进度存在自己的会话中,不会和其他用户的任务混淆。
- 前端轮询:用
fetch定期请求进度接口,拿到数据后更新页面,任务完成后自动触发下载。 - 临时文件处理:压缩包临时存在服务器,下载完成后立即删除,避免占用存储空间。
内容的提问来源于stack exchange,提问作者MyPing0
相关产品推荐
相关产品推荐

