【问题标题】:Importing large number of pdfs into mPDF is causing /fopen to fail将大量 pdf 导入 mPDF 导致 /fopen 失败
【发布时间】:2021-01-06 13:29:26
【问题描述】:

编辑:在 Jan 的帮助下更新

编辑:代码不会失败,直到 $mpdf->Output($max . ' imports .pdf', 'D');被称为

PHP 7.1 版

MPDF 版本 8.0.10

Setasign fpdi_pdf-parser 版本 2.0.4 错误

无法打开文件 (fopen) Compnay Induction (checklist) th v2.pdf 无法打开文件(fopen)员工信息注册.pdf 无法打开文件 (fopen) Coshh 列表 v7.pdf 无法打开文件 (fopen) 企业能力表.pdf

下面是我的代码的 sn-p,它会产生错误

function addCustomForms($forms, $mpdf, $Customer)
{
    if ($forms) {
        $handles = [];
        foreach ($forms as $CustomPolicyForm) {
            if (is_file('../cdata/' . $Customer->reference . '/policy-forms/' . $CustomPolicyForm->form)) {
                try {
                    $h = \fopen('../cdata/' . $Customer->reference . '/policy-forms/' . $CustomPolicyForm->form, 'rb');
                    // File not Opened
                    if ($h === false) {
                            echo 'Failed to open file (fopen) ' . $CustomPolicyForm->form . '<br/>';
                            return false;
                        } else {

                        $stream = new \setasign\Fpdi\PdfParser\StreamReader($h, false);
                        $pagecount = $mpdf->setSourceFile($stream);
                        if ($pagecount > 0) {
                            for ($i = 1; $i <= ($pagecount); $i++) {
                                $mpdf->AddPage($CustomPolicyForm->orientation);
                                if ($i == 1) {
                                    $html = '<h3 style="color: #ffffff;">' . sd($CustomPolicyForm->title) . '</h3>';
                                    $mpdf->WriteHTML($html);
                                }
                                try {
                                    $import_page = $mpdf->importPage($i);
                                    try {
                                        $mpdf->useTemplate($import_page);

                                    } catch (Exception $e) {
                                        echo 'Not worked for ' . $CustomPolicyForm->title .'<br/>';
                                        return false;
                                    }
                                } catch (Exception $e) {
                                    echo 'importPage failed for ' . $CustomPolicyForm->title . ' Page ' . $i .'<br/>';
                                    return false;
                                }
                            }
                            $handles[] = $h;

                        } else {
                            echo 'Page count is less than 1 for ' . $CustomPolicyForm->form .'<br/>';
                            return false;

                        }
                    }
                } catch
                (Exception $e) {
                    echo 'Failed to set source file for ' . $CustomPolicyForm->form . '<br/>';
                    return false;
                }
            } else {
                echo 'File not found: ' . $CustomPolicyForm->form .'<br/>';
                return false;
            }
            $pagecount = null;
        }
        return $handles;

    } else {
        // No forms found
        echo 'No Forms Found';
        return false;
    }
}

$mpdf->Output(Customer::find_by_id($Policy->customer_id)->customer . '.pdf', 'D');
        \fclose($handles);

更改 TOCuseLinking' => false 隐藏/解决了问题,但我不确定首先是什么导致了问题。

我使用的测试文件是https://1drv.ms/b/s!ApXTTaxD_QQPj7kwpxX3G4a5-2Ir2Q?e=WJRMi3(这是一个空白的PDF)

有人指点一下吗?

【问题讨论】:

    标签: php mpdf


    【解决方案1】:

    mPDF 的 TOC 功能试图通过克隆整个实例并重置它来发挥一些魔力……我没有深入研究它。但是由此对流阅读器的引用是不确定的。由于对象的数量,稍后会触发垃圾收集器,这会触发 __destruct() 方法,然后关闭流句柄。

    要绕过这个问题,您需要控制文件句柄。您的示例可以这样重写:

    $h = \fopen('BLANK PDF.pdf', 'rb');
    $stream = new \setasign\Fpdi\PdfParser\StreamReader($h, false);
    
    for ($count = 0; $count < $max; $count++) {
        $pagecount = $mpdf->setSourceFile($stream);
        if ($pagecount > 0) {
            $mpdf->AddPage();
            for ($i = 1; $i <= ($pagecount); $i++) {
                if ($i == 1) {
                    $html = '<h3 style="color: #000;">' . htmlentities('Blank PDF ' . $count) . '</h3>';
                    $mpdf->WriteHTML($html);
                }
                $import_page = $mpdf->importPage($i);
                $mpdf->useTemplate($import_page);
    
            }
        } else {
            return false;
        }
    }
    $mpdf->Output($max . ' imports .pdf', 'F');
    
    fclose($h);
    

    如果您需要处理不同的文件,请将句柄存储在数组中以保留它们的引用。

    您还应该注意到操作系统对打开的文件句柄有限制。 FPDI 受此限制。

    【讨论】:

    • 感谢您的助手 Jan。但它仍然没有为我工作。我已经用我正在调用的函数和错误(由我设置,所以我可以看到它发生在哪里)更新了上面的代码。
    • 您在 foreach() 循环中打开和关闭句柄,这肯定是错误的!您还指示读取器实例在未设置时自动关闭句柄(snd 参数需要为 false!)...我根本没有看到对 Output() 的调用?在调用Output() 之前,需要保留所有文件句柄。正如我的回答中所写,您应该将它们收集在一个数组中。
    • 再次感谢 Jan。我现在将句柄返回到数组中,并将代码修改为 false。我正在考虑如何获得在 foreach 循环外侧打开句柄的功能。有趣的是,如果包含的 PDF 只是文本(添加了 14 页纯文本 pdf),则代码有效,或者如果我减少导入的文件数量,它也有效。它以 38 失败,但与 32 一起工作。使用 38,我再次遇到无限循环错误。抱歉,我对此有点慢。
    • 处理的数据越多,越早触发GC。您的数组也可能从 GC 中收集,因为它仅在函数范围内使用。只要 PDF 生成完成,请确保您持有对文件句柄的引用。
    • 简,我的朋友,你是明星! GC 导致我出现错误并使用 gc_collect_cycles(); (无论是否正确!)已经解决了我的问题!非常感谢您对我和我的问题的坚持。
    猜你喜欢
    • 2017-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    相关资源
    最近更新 更多