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

如何使用dompdf一键生成500+个多页PDF,解决大数据量生成超时问题

优化方案

1. 核心性能问题修复

  • 消除循环内N+1查询:将所有商品批量查询数据,避免每个商品单独发起4次数据库请求,100个商品可将查询次数从400+次降到4次
  • 移除冗余格式转换:Laravel查询结果直接调用toArray()即可转数组,无需json_encode+json_decode的额外开销
  • 移除PDF多余操作:不需要同时调用save()和stream(),仅存储本地PDF时只保留save()即可,stream()是用于输出到浏览器的多余操作,还会占用额外IO资源

2. 优化后后端代码

// 临时调高执行时间和内存限制,可按需调整参数
set_time_limit(0);
ini_set('memory_limit', '1024M');

$finalItems = ['1','2','3','4','5','6',..............];
$site_id = 你的站点ID;
$fromDate = $_POST['fromDate'];
$toDate = $_POST['toDate'];
$pdf_name = [];

// 批量查询所有商品的group信息,一次查询完成
$grpList = DB::table('item_master')
    ->select('item_name', 'group')
    ->whereIn('item_name', $finalItems)
    ->get()
    ->keyBy('item_name') // 按商品名建索引,后续直接取值
    ->toArray();

// 批量查询三个业务表所有商品的对应数据,一次查询完成
$saleDataAll = DB::table('sale_data')
    ->whereIn('item_name', $finalItems)
    ->where('site_id', $site_id)
    ->whereBetween('bill_date',[$fromDate,$toDate])
    ->get()
    ->groupBy('item_name') // 按商品名分组
    ->toArray();

$purchaseDataAll = DB::table('purchase_data')
    ->whereIn('item_name', $finalItems)
    ->where('site_id', $site_id)
    ->whereBetween('bill_date',[$fromDate,$toDate])
    ->get()
    ->groupBy('item_name')
    ->toArray();

$stockTrfAll = DB::table('stock_transfer')
    ->whereIn('item_name', $finalItems)
    ->where('site_id', $site_id)
    ->whereBetween('bill_date',[$fromDate,$toDate])
    ->get()
    ->groupBy('item_name')
    ->toArray();

// 循环生成PDF,循环内无数据库查询操作
foreach($finalItems as $item){
    $sales = $saleDataAll[$item] ?? [];
    $purchase = $purchaseDataAll[$item] ?? [];
    $stock = $stockTrfAll[$item] ?? [];
    $res = array_merge($sales, $purchase, $stock);

    $groupName = $grpList[$item]['group'] ?? ''; 
    $pdf = PDF::loadView('myPDF', compact('res'));
    $pdf->setPaper('a3', 'landscape');
    // 仅保留本地存储逻辑,移除多余的stream操作
    $pdf->save(public_path().'/pdf/item_'.$item.'.pdf');
    $pdf_name[] = 'item_'.$item.'.pdf';
}

3. 优化后视图代码

<?php $last_balance = 初始化你的期初余额; ?>
@foreach(array_chunk($res, 25) as $chunk) {{-- 按每页25行拆分,和你的展示规则对应 --}}
    <table>
        {{-- 可在此处补充每页表头逻辑 --}}
        @foreach($chunk as $item)
        <tr>
            <td style="width:2%"></td>
            <td style="width:5.10%"></td>
            <td style="width:9.10%"></td>
            <td style="width:7.10%"></td>
            <td style="width:5.10%">{{ $item['batch_no'] }}</td>
            <td style="width:5.10%">{{ $item['mfg_date'] }}</td>
            <td style="width:5.10%">{{ $item['exp_date'] }}</td>
            <td style="width:5.10%"></td>
            <td style="width:5.10%"></td>
            <td style="width:5.10%"></td>
            <td style="width:3.10%">{{ $last_balance }}</td>
            <td style="width:5.10%"></td>
            <td style="width:3.10%">{{ $item['quantity_in_kgltr'] }}</td>
            <td style="width:4.10%"></td>
            <td style="width:4.10%"></td>
            <td style="width:5.10%">
                <?php 
                $tocl = (int)$item['quantity_in_kgltr'];
                $last_balance -= $tocl;
                echo $last_balance;
                ?>
            </td>
            <td style="width:5.10%">{{ $item['bill_no'] }}</td>
            <td style="width:5.10%">{{ date('d-m-Y', strtotime($item['bill_date'])) }}</td>
            <td style="width:8.10%">{{ $item['sales_to_customer_name'] }}</td>
            <td style="width:3.10%"></td>
        </tr>
        @endforeach
    </table>
    {{-- 可在此处添加CSS分页规则:page-break-after: always 控制PDF分页 --}}
@endforeach

4. 进阶优化(数据量更大仍超时可使用)

  • 改用队列异步生成PDF:用户触发任务后后台逐个生成,生成完成后通知用户下载,完全避免前端超时
  • 单商品数据超过2000行时,拆分生成多个PDF,最后合并为一个,避免单次PDF生成负载过高
  • 给三个业务表的item_name、site_id、bill_date字段加联合索引,大幅提升查询速度

内容的提问来源于stack exchange,提问作者Vicks

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 07:54:09