React JS中如何正确下载PDF与DOCX文件?解决文件损坏问题
React下载Flask返回的DOCX/PDF文件损坏问题
我在React中尝试根据Flask后端的响应下载DOCX或PDF文件,但下载后的文件总是损坏。
前端React代码
const { isDownloadPdfDocumentSuccess, downloadPdfDocument, isDownloadDocDocumentSuccess, downloadDocDocument, } = useSelector(state => state.document) useEffect(() => { if(isDownloadPdfDocumentSuccess || isDownloadDocDocumentSuccess) { downloadDocument(downloadPdfDocument || downloadDocDocument, "application/pdf" || "application/vnd.openxmlformats- officedocument.wordprocessingml.document"); } }, [isDownloadPdfDocumentSuccess, isDownloadDocDocumentSuccess]); function downloadDocument(downloadLink, downloadType) { const documentBlob = new Blob([downloadLink], { type: downloadType }); const url = window.URL.createObjectURL(documentBlob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = document_title; document.body.appendChild(anchor); anchor.click(); document.body.removeChild(anchor); window.URL.revokeObjectURL(url) ; }
后端Flask代码
@app.route('/download_document_docx/<request_document_id>', methods=['GET']) @token_required def download_document_docx(current_user, request_document_id): ''' Docx document preparation code ''' return send_file(docx_file_path, as_attachment=True, mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document")
问题分析及修复方案
1. 核心问题
- 请求未指定响应类型,后端返回的二进制文件被默认转成UTF-8字符串,导致Blob创建时数据损坏。
- MIME类型判断逻辑错误,
"application/pdf" || "其他类型"永远返回第一个值,DOCX文件会被错误标记为PDF类型。
2. 修复步骤
(1)请求阶段指定响应类型
在Redux Action发起请求时,必须设置responseType: 'blob',确保直接获取二进制Blob数据:
// PDF请求示例Action export const fetchPdf = (docId) => async (dispatch) => { try { const res = await axios.get(`/download_document_pdf/${docId}`, { responseType: 'blob' // 关键配置 }); dispatch({ type: 'DOWNLOAD_PDF_SUCCESS', payload: res.data // 直接存储Blob对象 }); } catch (err) { console.error('PDF下载失败', err); } }; // DOCX请求示例Action export const fetchDocx = (docId) => async (dispatch) => { try { const res = await axios.get(`/download_document_docx/${docId}`, { responseType: 'blob' }); dispatch({ type: 'DOWNLOAD_DOCX_SUCCESS', payload: res.data }); } catch (err) { console.error('DOCX下载失败', err); } };
(2)修正组件下载逻辑
拆分PDF和DOCX的下载判断,确保MIME类型与文件名匹配:
const { isDownloadPdfDocumentSuccess, downloadPdfDocument, isDownloadDocDocumentSuccess, downloadDocDocument, } = useSelector(state => state.document); useEffect(() => { if (isDownloadPdfDocumentSuccess) { downloadDocument(downloadPdfDocument, 'application/pdf', `${document_title}.pdf`); } if (isDownloadDocDocumentSuccess) { downloadDocument(downloadDocDocument, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', `${document_title}.docx`); } }, [isDownloadPdfDocumentSuccess, isDownloadDocDocumentSuccess, document_title]); function downloadDocument(blobData, mimeType, fileName) { const url = window.URL.createObjectURL(blobData); // 直接使用已有Blob const anchor = document.createElement('a'); anchor.href = url; anchor.download = fileName; document.body.appendChild(anchor); anchor.click(); document.body.removeChild(anchor); window.URL.revokeObjectURL(url); }
(3)后端优化(可选)
在Flask的send_file中明确指定下载文件名,避免前端文件名混乱:
@app.route('/download_document_docx/<request_document_id>', methods=['GET']) @token_required def download_document_docx(current_user, request_document_id): # Docx文件准备逻辑 return send_file( docx_file_path, as_attachment=True, mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", attachment_filename=f"document_{request_document_id}.docx" )
内容的提问来源于stack exchange,提问作者Davit 1997
相关产品推荐
相关产品推荐

