如何通过单路由使用Barryvdh\DomPDF实现多PDF文件下载?
需求可行性与实现方案
你的需求是可行的,但原代码思路存在问题——HTTP协议规定一个请求只能对应一个响应,队列属于异步执行逻辑,无法直接向前端返回多个PDF流。下面是几种可落地的实现思路:
方案一:前端触发多请求(实现“每个PDF在新页打开”)
后端提供单个PDF的下载接口,前端在用户点击“下载全部”时,循环调用该接口并打开新标签页,以此实现多PDF分别展示的效果。
后端代码
// 单个PDF下载路由 Route::get('/person/{person}/letter/{letter}/download', [PersonController::class, 'downloadSingle'])->name('person.letter.download'); // 单个PDF下载方法 public function downloadSingle(Person $person, Letter $letter) { return Pdf::loadView('cancellation-letter', [ 'letter' => $letter, ])->stream($letter->uuid . '.pdf'); } // 下载全部的入口方法 public function downloadAll(Person $person) { return view('download-all', compact('person')); }
前端代码(Blade视图示例)
<button id="downloadAllBtn">下载全部文件</button> <script> document.getElementById('downloadAllBtn').addEventListener('click', function() { @foreach($person->letter as $letter) // 每个请求打开新空白标签页 window.open("{{ route('person.letter.download', [$person, $letter]) }}", '_blank'); @endforeach }); </script>
方案二:合并为单个PDF文件
把所有信件内容合并成一个PDF返回,用户下载一个包含所有页面的文件,适合不需要单独保存每个PDF的场景。需要借助setasign/fpdi扩展实现PDF合并。
步骤与代码
- 安装依赖:
composer require setasign/fpdi - 控制器实现:
use setasign\Fpdi\Fpdi; public function downloadAll(Person $person) { $fpdi = new Fpdi(); foreach ($person->letter as $letter) { // 生成单个PDF的二进制内容 $pdfContent = Pdf::loadView('cancellation-letter', ['letter' => $letter])->output(); // 将当前PDF的所有页面导入到合并文档中 $pageCount = $fpdi->setSourceData($pdfContent); for ($i = 1; $i <= $pageCount; $i++) { $templateId = $fpdi->importPage($i); $size = $fpdi->getTemplateSize($templateId); $fpdi->AddPage($size['orientation'], [$size['width'], $size['height']]); $fpdi->useTemplate($templateId); } } // 返回合并后的PDF文件 return response($fpdi->Output(), 200, [ 'Content-Type' => 'application/pdf', 'Content-Disposition' => 'attachment; filename="all-person-letters.pdf"' ]); }
方案三:打包为ZIP压缩包
生成所有独立PDF文件并打包成ZIP返回,用户下载后可解压得到每个单独的PDF文件。
控制器代码
use Illuminate\Support\Facades\Storage; use ZipArchive; public function downloadAll(Person $person) { $zip = new ZipArchive(); $zipFileName = "person-{$person->id}-letters.zip"; $zipPath = storage_path('app/temp/' . $zipFileName); // 创建临时存储目录 Storage::makeDirectory('temp'); if ($zip->open($zipPath, ZipArchive::CREATE) === TRUE) { foreach ($person->letter as $letter) { $pdfContent = Pdf::loadView('cancellation-letter', ['letter' => $letter])->output(); // 将PDF内容添加到压缩包 $zip->addFromString("{$letter->uuid}.pdf", $pdfContent); } $zip->close(); } // 返回ZIP文件,下载完成后自动删除临时文件 return response()->download($zipPath)->deleteFileAfterSend(true); }
内容的提问来源于stack exchange,提问作者GrayhoundButPurple
相关产品推荐
相关产品推荐

