Laravel如何向React前端返回多个docx文件?
Laravel后端向React前端返回多文件问题
使用技术栈:PHP(Laravel框架)、JavaScript(React框架)
我需要从Laravel后端向React前端返回多个docx文件,目前单文件返回功能正常,但不知道如何实现多文件返回。$generatedFiles是包含所有待返回docx文件路径的数组。
现有代码
后端代码(待修改)
$generatedFiles[] = public_path() . '/' . $fileName . '.docx'; return response()->file(此处需返回generatedFiles中的所有文件);
单文件返回可行代码
return response()->file(public_path() . '/' . $fileName . '.docx');
前端接收响应代码(React)
export const postProject = (datas) => async (dispatch) => { console.log(datas); dispatch(setPostLoading(true)); await axios({ method: 'post', url: `${process.env.REACT_APP_API}/api/projects`, data: datas, responseType: 'blob', headers: { Accept: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', }, }).then((res) => { if (!res.data) { dispatch(setError(true)); } else { var blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', }); saveAs(blob, 'test.docx'); } }); dispatch(setPostLoading(false)); };
已尝试的方案
- 打包Zip文件返回,但报错“压缩的Zip文件夹无效”
相关代码:
foreach ($generatedFiles as $file) { $zip->addFile($file, basename($file)); // 将每个生成的docx文件添加到Zip中 } $zip->close(); // 读取Zip文件内容并作为响应返回 $fileContents = file_get_contents($zipPath); $response = response()->make($fileContents, 200, [ 'Content-Type' => 'application/zip', 'Content-Disposition' => 'attachment; filename="generated_files.zip"', ]); // 清理临时文件 unlink($zipPath); return $response;
- 返回JSON格式的文件路径数组,但前端遍历后不知道如何处理每个元素
相关代码:
return response()->json($generatedFiles);
解决方案
方案一:修复Zip打包返回(推荐)
之前的Zip方案报错,大概率是文件创建过程存在权限问题、文件不存在或Zip实例初始化错误,按以下步骤调整:
后端代码调整
use ZipArchive; use Illuminate\Support\Facades\Storage; // 初始化Zip实例 $zip = new ZipArchive(); $zipName = 'generated_files.zip'; // 用storage目录存储临时Zip,避免权限问题 $zipPath = storage_path('app/temp/' . $zipName); // 确保临时目录存在 if (!file_exists(storage_path('app/temp'))) { mkdir(storage_path('app/temp'), 0755, true); } // 打开Zip文件,CREATE模式自动创建/覆盖文件 if ($zip->open($zipPath, ZipArchive::CREATE) === TRUE) { foreach ($generatedFiles as $file) { // 先验证文件存在,避免无效文件损坏Zip if (file_exists($file)) { $zip->addFile($file, basename($file)); } } $zip->close(); // 用Laravel自带的file响应返回,自动处理头信息 $response = response()->file($zipPath, [ 'Content-Disposition' => 'attachment; filename="' . $zipName . '"', 'Content-Type' => 'application/zip', ]); // 响应结束后清理临时文件,避免占用空间 register_shutdown_function(function () use ($zipPath) { if (file_exists($zipPath)) { unlink($zipPath); } }); return $response; } else { return response()->json(['error' => '无法创建压缩文件'], 500); }
前端代码调整
修改请求头和响应逻辑,适配Zip类型:
export const postProject = (datas) => async (dispatch) => { console.log(datas); dispatch(setPostLoading(true)); try { const res = await axios({ method: 'post', url: `${process.env.REACT_APP_API}/api/projects`, data: datas, responseType: 'blob', headers: { Accept: 'application/zip', // 调整Accept头为Zip类型 }, }); if (!res.data) { dispatch(setError(true)); return; } const blob = new Blob([res.data], { type: 'application/zip' }); saveAs(blob, 'generated_files.zip'); } catch (error) { dispatch(setError(true)); } finally { dispatch(setPostLoading(false)); } };
方案二:返回文件URL数组,前端逐个下载
如果不想用Zip,可返回文件的可访问URL,前端循环请求下载:
后端代码
// 将本地路径转为前端可访问的URL $fileUrls = array_map(function ($file) { return asset(str_replace(public_path(), '', $file)); }, $generatedFiles); return response()->json(['files' => $fileUrls]);
前端代码
import axios from 'axios'; import { saveAs } from 'file-saver'; export const postProject = (datas) => async (dispatch) => { console.log(datas); dispatch(setPostLoading(true)); try { const res = await axios({ method: 'post', url: `${process.env.REACT_APP_API}/api/projects`, data: datas, }); if (!res.data?.files) { dispatch(setError(true)); return; } // 遍历URL逐个下载 for (const url of res.data.files) { const fileRes = await axios({ url, responseType: 'blob', }); const fileName = url.split('/').pop(); const blob = new Blob([fileRes.data], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }); saveAs(blob, fileName); } } catch (error) { dispatch(setError(true)); } finally { dispatch(setPostLoading(false)); } };
内容的提问来源于stack exchange,提问作者Elodie
相关产品推荐
相关产品推荐

