使用 Dompdf 一键生成大量 PDF 文件的优化方案

使用 Dompdf 一键生成大量 PDF 文件的优化方案
最新回答
听萧与声

2026-06-10 16:44:40

使用 Dompdf 一键生成大量 PDF 文件的核心优化方案是迁移至命令行环境,配合set_time_limit(0)解除执行时间限制,并通过分块处理数据、优化渲染逻辑提升效率。 以下是具体实施步骤与关键优化点:

一、迁移至命令行环境
  • 原因:Web 服务器(如 Apache/Nginx)默认配置的超时时间较短(通常 30-60 秒),且需同时处理其他请求,长时间运行的 PDF 生成任务易导致资源阻塞。命令行环境(PHP CLI)无超时限制,可独占资源稳定运行。
  • 操作:

    创建独立脚本 generate_pdfs.php,通过 php generate_pdfs.php 执行。

    脚本开头添加 set_time_limit(0),确保无限执行时间。

    关闭输出缓冲(如 ob_implicit_flush(false)),减少内存占用。

二、数据库查询优化
  • 问题:循环内多次查询数据库(销售、采购、库存转移数据)导致 N+1 查询问题,数据量大时延迟显著。
  • 优化:

    合并查询:使用 UNION ALL 或单次查询获取所有数据,减少数据库往返次数。

    $sql = "SELECT * FROM sale_data WHERE item_name = ? AND site_id = ? AND bill_date BETWEEN ? AND ? UNION ALL SELECT * FROM purchase_data WHERE item_name = ? AND site_id = ? AND bill_date BETWEEN ? AND ? UNION ALL SELECT * FROM stock_transfer WHERE item_name = ? AND site_id = ? AND bill_date BETWEEN ? AND ?";$stmt = $pdo->prepare($sql);$stmt->execute(array_fill(0, 12, $item)); // 参数重复填充$res = $stmt->fetchAll(PDO::FETCH_ASSOC);

    分页处理:若数据量极大(如单 item 超 10 万行),按批次查询(如每次 1000 行),避免内存溢出。

三、HTML 渲染与 Dompdf 配置优化
  • 问题:复杂 HTML 结构或大量数据导致渲染耗时。
  • 优化:

    简化 HTML:移除冗余标签,使用 CSS 替代内联样式。

    分块渲染:将数据按页拆分,逐页渲染后合并 PDF(需 Dompdf 支持多页)。

    禁用缓存(开发环境):$options->set('isRemoteEnabled', true) 允许加载外部资源,但生产环境建议关闭。

    调整内存限制:脚本开头添加 ini_set('memory_limit', '1024M'),根据数据量调整。

四、错误处理与日志记录
  • 命令行环境无自动错误提示,需手动捕获异常:try { // 数据库查询与 PDF 生成逻辑} catch (PDOException $e) { file_put_contents('error.log', "Database error: " . $e->getMessage(), FILE_APPEND);} catch (Exception $e) { file_put_contents('error.log', "PDF error: " . $e->getMessage(), FILE_APPEND);}
  • 记录进度:每生成一个 PDF 输出日志,便于监控:echo date('Y-m-d H:i:s') . " - Generated PDF for $itemn";
五、参数传递与自动化
  • 从 Web 传递参数:

    文件传递:Web 页面将 $finalItems 序列化为 JSON 写入文件,脚本读取:$finalItems = json_decode(file_get_contents('params.json'), true);

    数据库传递:Web 页面将参数存入数据库表,脚本查询后标记已处理记录。

  • 定时任务:通过 Cron 定时执行脚本,实现自动化批量生成。
六、完整脚本示例<?phprequire_once 'vendor/autoload.php';use DompdfDompdf;use DompdfOptions;set_time_limit(0);ini_set('memory_limit', '1024M');// 数据库配置$pdo = new PDO("mysql:host=localhost;dbname=test", 'user', 'pass');$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);// 模拟参数(实际可从文件或数据库读取)$finalItems = ['item1', 'item2'];$site_id = 1;$fromDate = '2023-01-01';$toDate = '2023-12-31';foreach ($finalItems as $item) { try { // 合并查询数据 $sql = "SELECT * FROM sale_data WHERE item_name = ? AND site_id = ? AND bill_date BETWEEN ? AND ? UNION ALL SELECT * FROM purchase_data WHERE item_name = ? AND site_id = ? AND bill_date BETWEEN ? AND ? UNION ALL SELECT * FROM stock_transfer WHERE item_name = ? AND site_id = ? AND bill_date BETWEEN ? AND ?"; $stmt = $pdo->prepare($sql); $params = array_merge([$item, $site_id, $fromDate, $toDate], [$item, $site_id, $fromDate, $toDate], [$item, $site_id, $fromDate, $toDate]); $stmt->execute($params); $res = $stmt->fetchAll(PDO::FETCH_ASSOC); // 渲染 PDF $options = new Options(); $options->set('defaultFont', 'Arial'); $dompdf = new Dompdf($options); ob_start(); include 'myPDF.php'; // 包含优化后的 HTML 模板 $html = ob_get_clean(); $dompdf->loadHtml($html); $dompdf->setPaper('a3', 'landscape'); $dompdf->render(); // 保存文件 $pdfPath = "pdf/item_{$item}.pdf"; file_put_contents($pdfPath, $dompdf->output()); echo "Generated: $pdfPathn"; } catch (Exception $e) { file_put_contents('error.log', "Error for $item: " . $e->getMessage() . "n", FILE_APPEND); }}七、关键注意事项
  • PHP CLI 配置:确保 php.ini 中 max_execution_time 和 memory_limit 足够大。
  • Dompdf 版本:使用最新稳定版(如 2.0+),修复已知性能问题。
  • 测试环境验证:先在少量数据下测试脚本稳定性,再逐步扩展。

通过以上优化,可显著提升 Dompdf 批量生成 PDF 的效率与稳定性,避免超时与内存错误。