在当前的一个PHP项目中,我需要将一堆PDF文件打包到某种归档文件中,这样用户就可以一起下载它们了。因为Zip是最常见的,甚至是最基本的非IT Windows用户都知道的,所以我要找一个Zip存档。
我的代码如下
$invoices = getRequestedInvoices(); // load all requested invoices, this function is just for demonstration
// Create a temporary zip archive
$filename = tempnam("tmp","zip");
$zip = new \ZipArchive();
$zip->open($filename, \ZipArchive::OVERWRITE);
foreach($invoices as $invoice)
{
// create the pdf file and add it to the archive
$pdf = new InvoicePdf($invoice); // this is derived from \ZendPdf\PdfDocument
$zip->addFromString($pdf->getFilename(), $pdf->render()); // for clarification: the getFilename method creates a filename for the PDF based on the invoice's id
}
$zip->close();
header('Content-Type: application/zip');
header('Content-Length: ' . filesize($filename));
header('Content-Disposition: attachment; filename="invoices.zip"');
readfile($filename);
unlink($filename);
exit;
如果服务器有足够的内存,这个脚本就可以正常工作。不幸的是,我们的生产系统非常有限,所以脚本只能与几个PDF文件一起工作,但大多数时间它会耗尽内存并中止。在foreach循环的末尾添加unlink($pdf)没有帮助,所以我猜zipArchive对象正在耗尽内存。
我正试图向项目中添加尽可能少的依赖项,所以我希望能够用phps(php 5.4)自己的函数或zend framework 2中的函数来解决这个问题。我在寻找直接流式传输归档文件的方法(zip://stream包装器起初看起来不错,但它是只读的),但对于zip归档文件来说,这似乎是不可能的。
有人有主意吗?可能是一种不同但又广为人知的允许流式传输的归档类型?压缩不是必须的
如果内存有限,那么不要使用内置的zip文件。您可以改为使用exec("zip $filename file1 file2 file3"),这不受PHP内存限制的限制。为了防止shell注入攻击,您必须非常小心地使用各种文件名,但是它会给您提供压缩包,而不会因为内存不足错误而杀死php。
我考虑过,但是平台是独立的吗?当我们迁移到不同的服务器(具有未知属性)时,我需要牢记未来。
不是真的。一旦你开始处理shell,你基本上依赖于平台。
我必须找到一个快速解决这个问题的方法,所以尽管试图避免它,我还是不得不使用一个外部依赖。
我在phpzip项目(https://github.com/grant/phpzip)中找到了zipstream类,它可以很好地完成工作。
$zip = new \ZipStream("invoices.zip");
foreach($invoices as $invoice)
{
$pdf = new InvoicePdf($invoice);
$zip->addFile($pdf->render(), $pdf->getFilename());
}
$zip->finalize();
exit;