You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过单路由使用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合并。

步骤与代码

  1. 安装依赖:composer require setasign/fpdi
  2. 控制器实现:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:15:35