如何实现Flask中使用send_file下载文件后自动重定向至新页面?
解决点击下载链接同时触发下载和页面跳转的问题
我来帮你搞定这个问题!你遇到的核心矛盾是:HTTP响应一次只能返回一个结果(要么是下载文件,要么是页面重定向),所以后端没法同时完成这两个动作。我们得换个思路,让前端JavaScript来处理这两个并行的操作,同时还要做好防重复点击的处理。
问题根源分析
你的原代码里,send_file和redirect都是返回HTTP响应的操作,浏览器只能接收其中一个,所以没法同时触发下载和跳转。而且直接用<a>标签加target="_blank"的方式,也没法很好地控制跳转时机和禁用按钮。
具体解决方案
我们分两步修改:前端处理双动作触发+防重复点击,后端优化多用户隔离。
1. 修改前端HTML(conversion_complete.html)
用JavaScript监听按钮点击事件,先触发下载请求,再跳转页面,同时立即禁用按钮防止重复点击:
<html> <body> <h1>Conversion Complete</h1> <button id="downloadBtn" class='btn btn-default'>Download!</button> <script> document.getElementById('downloadBtn').addEventListener('click', function() { // 第一步:触发文件下载 const downloadTrigger = document.createElement('a'); downloadTrigger.href = '/download/'; downloadTrigger.download = ''; // 让浏览器使用后端返回的文件名 downloadTrigger.style.display = 'none'; document.body.appendChild(downloadTrigger); downloadTrigger.click(); document.body.removeChild(downloadTrigger); // 第二步:禁用按钮,防止重复点击 this.disabled = true; this.textContent = 'Downloading...'; // 第三步:延迟跳转,确保下载请求已发送到后端 setTimeout(() => { window.location.href = '/'; // 这里可以改成你想要跳转的目标页面,比如首页 }, 600); }); </script> </body> </html>
2. 优化后端代码(解决多用户冲突问题)
原代码里把下载文件的信息存在download_video函数的属性里,这会导致多个用户同时使用时互相覆盖。我们改用session来存储每个用户的下载信息,同时下载完成后清理文件和session:
from flask import Flask, render_template, redirect, url_for, session, request, flash, send_file from flask_wtf import FlaskForm from wtforms import StringField, SubmitField, BooleanField from pytube import YouTube import os import random, string import re app = Flask(__name__) app.config['SECRET_KEY'] = 'mysecretkey' class url_form(FlaskForm): link = StringField('Enter link of YouTube video you would like to convert...') audio = BooleanField('Audio Only') submit = SubmitField('Convert') def download_video(link, just_audio): yt = YouTube(link) download_path = 'conversions/' # 创建conversions目录如果不存在 os.makedirs(download_path, exist_ok=True) if just_audio: stream = yt.streams.filter(only_audio=True).first() else: stream = yt.streams.filter(adaptive=True).first() title = yt.title clean_title = re.sub('[^a-zA-Z0-9 \n\.]', '', title) thumbnail = yt.thumbnail_url file_path = stream.download(filename=clean_title, output_path=download_path) # 返回下载信息字典,存在session中 return { 'title': title, 'clean_title': clean_title, 'thumbnail': thumbnail, 'file_path': file_path } @app.route('/', methods=['GET', 'POST']) def index(): form = url_form() if form.validate_on_submit(): link = form.link.data just_audio = form.audio.data download_info = download_video(link, just_audio) # 把当前用户的下载信息存入session session['download_info'] = download_info return redirect(url_for('conversion_complete')) return render_template('index.html', form=form) @app.route('/conversion_complete') def conversion_complete(): # 如果session里没有下载信息,直接跳回首页 if not session.get('download_info'): flash('No conversion found!') return redirect(url_for('index')) return render_template('conversion_complete.html') @app.route('/download/') def download(): download_info = session.get('download_info') if not download_info: flash('No download available!') return redirect(url_for('index')) file_path = download_info['file_path'] filename = download_info['clean_title'] + '.mp4' # 下载完成后清理文件和session def cleanup(): if os.path.exists(file_path): os.remove(file_path) session.pop('download_info', None) return send_file( file_path, attachment_filename=filename, as_attachment=True, after_send=cleanup )
方案说明
- 前端逻辑:通过创建隐藏的
<a>标签触发下载,避免页面跳转中断下载请求;立即禁用按钮彻底防止重复点击;延迟跳转确保下载请求已经发送到后端。 - 后端优化:用
session隔离每个用户的下载信息,避免多用户冲突;下载完成后自动删除文件和session中的记录,节省服务器空间同时防止重复下载。
内容的提问来源于stack exchange,提问作者James Unsworth
相关产品推荐
相关产品推荐

