如何使用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
相关产品推荐
相关产品推荐

