将大量 pdf 导入 mPDF 导致 /fopen 失败

问题描述

编辑:在 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 listings v7.pdf 无法打开文件 (fopen) Emp 能力表.pdf

下面是我产生错误代码片段

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)

有大佬指点一下吗?

解决方法

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 受此限制。