React如何获取Flask后端转换后的文件并提供下载功能
实现React组件DisplayConvertedFile的完整方案
针对开发类似Convertio的文件转换服务需求,以下是实现DisplayConvertedFile组件的具体方案,同时调整前后端配合逻辑以支持异步转换状态跟踪和文件下载:
一、前端部分实现
1. 扩展AppContext状态
首先在全局Context中添加任务ID和转换状态的管理,用于组件间共享数据:
import { createContext, useState } from 'react'; export const AppContext = createContext(); export function AppProvider({ children }) { const [file, setFile] = useState(null); const [taskId, setTaskId] = useState(null); const [conversionStatus, setConversionStatus] = useState(null); // 可选值:pending/completed/failed return ( <AppContext.Provider value={{ file, setFile, taskId, setTaskId, conversionStatus, setConversionStatus }}> {children} </AppContext.Provider> ); }
2. 修改ConvertFileButton组件
调整原按钮组件,处理后端响应获取任务ID并更新全局状态:
export default function ConvertFileButton() { const { file, setTaskId, setConversionStatus } = useContext(AppContext); const handleConvert = async () => { if (!file) return; const formData = new FormData(); formData.append('file', file); try { const response = await fetch('http://localhost:5000/transcript', { method: 'POST', body: formData }); // 从后端重定向的Location头中提取task_id const locationHeader = response.headers.get('Location'); if (locationHeader) { const taskId = new URL(locationHeader).searchParams.get('id'); setTaskId(taskId); setConversionStatus('pending'); } } catch (error) { console.error('转换请求失败:', error); setConversionStatus('failed'); } }; return ( <MyButton onClick={handleConvert} disabled={!file}>CONVERT</MyButton> ); }
3. 实现DisplayConvertedFile组件
该组件负责轮询任务状态,转换完成后提供下载按钮:
import { useEffect, useContext } from 'react'; import { AppContext } from './AppContext'; import MyButton from './MyButton'; // 假设你的按钮组件路径 export default function DisplayConvertedFile() { const { taskId, conversionStatus, setConversionStatus } = useContext(AppContext); // 轮询任务状态,每2秒查询一次 useEffect(() => { if (!taskId || ['completed', 'failed'].includes(conversionStatus)) return; const interval = setInterval(async () => { try { const res = await fetch(`http://localhost:5000/status?id=${taskId}`); const data = await res.json(); if (data.status === 'completed') { setConversionStatus('completed'); clearInterval(interval); } else if (data.status === 'failed') { setConversionStatus('failed'); clearInterval(interval); } } catch (err) { console.error('查询任务状态失败:', err); clearInterval(interval); } }, 2000); return () => clearInterval(interval); }, [taskId, conversionStatus, setConversionStatus]); // 触发文件下载逻辑 const handleDownload = async () => { try { const res = await fetch(`http://localhost:5000/get?id=${taskId}`); const blob = await res.blob(); const downloadUrl = window.URL.createObjectURL(blob); // 创建临时a标签触发下载 const link = document.createElement('a'); link.href = downloadUrl; // 从响应头获取文件名,无则使用默认命名 const fileName = res.headers.get('Content-Disposition')?.split('filename=')[1] || `converted_${taskId}`; link.download = fileName; document.body.appendChild(link); link.click(); // 清理资源 window.URL.revokeObjectURL(downloadUrl); document.body.removeChild(link); } catch (err) { console.error('下载文件失败:', err); } }; // 根据状态渲染不同内容 if (!taskId) return <div>请先上传并转换文件</div>; if (conversionStatus === 'pending') return <div>转换中,请稍候...</div>; if (conversionStatus === 'failed') return <div>转换失败,请重试</div>; return ( <div className="converted-file-container"> <p>转换完成!</p> <MyButton onClick={handleDownload}>下载转换后文件</MyButton> </div> ); }
二、后端接口调整
原后端需要添加任务状态查询接口,并调整现有接口逻辑以支持前端交互:
1. 添加任务状态查询接口
from flask import jsonify, request import uuid # 示例任务存储(生产环境建议用数据库/Redis) task_storage = {} @mod.route('/status', methods=['GET']) def get_task_status(): task_id = request.args.get('id') task = task_storage.get(task_id) if not task: return jsonify({'status': 'not_found'}), 404 return jsonify({'status': task['status']})
2. 调整/get接口,根据task_id返回对应文件
@mod.route('/get', methods=['GET']) def get_result(): task_id = request.args.get('id') task = task_storage.get(task_id) if not task or task['status'] != 'completed': return jsonify({'error': '任务未完成或不存在'}), 400 # 设置下载文件名,方便用户识别 return send_file( task['file_path'], as_attachment=True, download_name=f"converted_{task['original_filename']}" )
3. 完善/transcript接口的任务存储逻辑
@mod.route('/transcript', methods=['POST']) def create_transcript_task(): # 处理上传文件 uploaded_file = request.files.get('file') if not uploaded_file: return jsonify({'error': '未上传文件'}), 400 # 生成唯一任务ID task_id = str(uuid.uuid4()) # 初始化任务状态,保存到存储 task_storage[task_id] = { 'status': 'pending', 'original_filename': uploaded_file.filename, 'file_path': '' # 后续转换完成后更新为实际文件路径 } # 启动异步转换任务(生产环境建议用Celery等异步框架) # convert_file.delay(uploaded_file, task_id) # 保持原重定向逻辑,或直接返回task_id的JSON响应 return redirect(url_for('backend.status_page') + f'?id={task_id}')
注意:生产环境中,异步转换任务建议使用Celery+Redis/RabbitMQ实现,避免阻塞Flask主线程。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

