PHP Imagick在将PDF转换为图像时挂起整个服务器

PHP Imagick在将PDF转换为图像时挂起整个服务器,php,imagick,Php,Imagick,我使用PHP上的扩展名Imagick将PDF文件转换为图像,具体来说是PNG文件。 转换过程中,整个服务器始终挂起。我所做的是上传一堆PDF文件,循环浏览它和它的页面,将其转换为图像 这是代码 foreach ($uploaded_file as $key => $value) { $upload_file_path = $upload_path.'/'.$value->name; $imagick = new Imagick(); $imagick->setRes

我使用PHP上的扩展名Imagick将PDF文件转换为图像,具体来说是PNG文件。 转换过程中,整个服务器始终挂起。我所做的是上传一堆PDF文件,循环浏览它和它的页面,将其转换为图像

这是代码

foreach ($uploaded_file as $key => $value) {
  $upload_file_path = $upload_path.'/'.$value->name;
  $imagick = new Imagick();
  $imagick->setResourceLimit(6, 1);
  $imagick->setResolution(300, 300);
  $imagick->readImage($upload_file_path);
  $pages = $imagick->getNumberImages();

  for ($x=0; $x<$pages; $x++) {
    $imagick->readImage($upload_file_path.'['.$x.']');
    $imagick->setImageFormat('png');
    $imagick->writeImage($image_path.'/page-'.$x.'.png');
  }

  $imagick->clear();
  $imagick->destroy();
}
foreach($key=>value){
$upload_file_path=$upload_path.'/'.$value->name;
$imagick=新的imagick();
$imagick->setResourceLimit(6,1);
$imagick->setResolution(300300);
$imagick->readImage($upload\u file\u path);
$pages=$imagick->getNumberImages();
对于($x=0;$xreadImage($upload_file_path.['.$x.]');
$imagick->setImageFormat('png');
$imagick->writeImage($image_path./page-'.$x..png');
}
$imagick->clear();
$imagick->destroy();
}

转换正在进行中,我无法访问服务器上的其他站点,转换时间太长。请提供帮助。谢谢!

根据PDF的内容,转换可能确实非常繁重。您对此无能为力

您已经尝试将线程总数降低到2个,但是仍然需要考虑内存、I/O延迟和CPU资源,并且您不知道服务器是否可以使用两个以上的线程

您可以尝试使用
nice
ionice
降低正在运行进程的I/O和CPU优先级,如果它们可用。您需要一个合适的平台(Linux、BSD或类似平台),并访问
nice
ionice
工具;您需要一个专用的可执行PHP进程(可能不是FastCGI),并能够查询其PID

在Linux系统上,您可能会尝试将转换外包到ImageMagick二进制文件,并将其包装在
nice
ionice
中,以使转换变得简单而缓慢

另一种可能性是看你是否可以使用ghostscript来代替Imagick。

将PDF转换为PNG的速度更快,你可以试试

我为您制作了一个示例程序:

#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';

use Jcupitt\Vips;

for ($i = 1; $i < count($argv); $i++) {
  $image = Vips\Image::newFromFile($argv[$i]);
  $n_pages = $image->get("n-pages");
  echo($argv[$i] . " has " . $n_pages . " pages\n");

  for ($n = 0; $n < $n_pages; $n++) {
    echo("  rendering page " . $n . " ...\n");
    $page = Vips\Image::newFromFile($argv[$i], [
      "dpi" => 300,
      "page" => $n,
      # this enables image streaming
      "access" => "sequential"
    ]);
    $page->writeToFile($argv[$i] . "_page_" . $n . ".png");
  }
}
因此,它在32秒内生成了58个PNG,最大需要110mb的ram。它不创建任何临时文件——110mb覆盖了所有内容

png是一种非常慢的文件格式。如果改为保存为jpg,则需要6秒左右的时间

我尝试了你的imagick代码的一个版本:

#!/usr/bin/env php
<?php

for ($i = 1; $i < count($argv); $i++) {
  $imagick = new Imagick();
  $imagick->setResourceLimit(6, 1);
  $imagick->setResolution(300, 300);
  $imagick->readImage($argv[$i]);
  $pages = $imagick->getNumberImages();
  echo($argv[$i] . " has " . $pages . " pages\n");

  for ($x = 0; $x < $pages; $x++) {
    echo("  rendering page " . $x . " ...\n");
    $imagick->readImage($argv[$i] . "[" . $x . "]");
    $imagick->setImageFormat("png");
    $imagick->writeImage($argv[$i] . "_page_" . $x . ".png");
  }

  $imagick->clear();
  $imagick->destroy();
} 

220秒(几乎慢了7倍)和260mb的内存。内存的使用并不是全部——在300 DPI的情况下,imagick将在/tmp中为每个页面生成一个65mb的文件,因此它总共需要大约5gb的存储空间。

我想分享我的发现和一个解决方案,我认为这是很有见地的

我注意到,每页创建一个新的
imagick
实例要比尝试读取整个文件,然后让它1)读取页面数量,2)让它在所有页面上迭代快得多

我只想要PDF文件的前10页。当我有一个50页的PDF文件时(大多数只是文本,只有450KB),你以何种方式阅读这个文件真的很重要

通过这种方式,它将从第一页开始阅读,并尝试阅读到10页。如果少于10个页面,只要imagick抛出错误,它就会意识到它已经到达页面的末尾,并停止迭代


让我对我的其他尝试有更多的了解

  • 我首先尝试读取整个pdf文件,然后使用
    getNumberImages()
    setIteratorIndex()
    设置循环并读取各个页面。对于imagick来说,阅读整本书需要很长时间,甚至还没有开始处理所有的页面

  • 也可以使用imagick的轻量级实例来查看有多少页面,这将是一个比让imagick尝试读取不存在的页面更整洁的解决方案:


  • 然而,这在我的测试中已经花费了10秒,只是为了阅读页数。这就是我使用上述方法的原因。

    您使用了多少pdf文件,平均有多少页?您在pdf中的阅读密度为300,是默认密度(72)的4倍多。因此,使用默认密度时,文件大小将增加16倍。如果您的PDF文件是大英寸,那么您可能超过了可用的RAM。有关处理大型图像的信息,请参阅。@admcfajn我有一个模板,其中我上载了32个PDF文件,共有95页,另一个模板包含95页。@fmw42是的,我正在以300的密度对其进行转换,以便在浏览器放大时图像质量不会下降。我试试你的建议。Thanks@fmw42顺便说一句,我使用了Imagick的函数setResourceLimit(),因为我读到它解决了我的问题,所以我想还有更多。我不确定使用ionice是否合适,因为我需要上传的文件在上传后进行转换。据我所知,ionice就像一个cron作业管理器,而且,没有ghostscript,Imagick无法读取PDF文件,所以我想我已经在使用它了。谢谢,我将尝试这个库,我使用PNG是因为当我转换为JPEG时,即使我更改了背景颜色,一些页面也会得到一个完整的黑色页面。在libvips跟踪器上打开一个问题如果你发现一个PDF不起作用,我会好奇地看到:我一次更新了一个页面的示例。这意味着它将适用于更改文档中页面大小的PDF。您好,我在windows上安装libvips时遇到了困难。我得到了windows二进制文件,我目前被困在如何使用PHP中,我不知道把这些二进制文件放在哪里。我需要复制php文件夹中的文件吗?顺便说一句,我使用的是xamppSorry,php VIP现在不能在windows上工作,粘合层还没有移植:修复起来应该不难,但需要有人自愿。
    #!/usr/bin/env php
    <?php
    
    for ($i = 1; $i < count($argv); $i++) {
      $imagick = new Imagick();
      $imagick->setResourceLimit(6, 1);
      $imagick->setResolution(300, 300);
      $imagick->readImage($argv[$i]);
      $pages = $imagick->getNumberImages();
      echo($argv[$i] . " has " . $pages . " pages\n");
    
      for ($x = 0; $x < $pages; $x++) {
        echo("  rendering page " . $x . " ...\n");
        $imagick->readImage($argv[$i] . "[" . $x . "]");
        $imagick->setImageFormat("png");
        $imagick->writeImage($argv[$i] . "_page_" . $x . ".png");
      }
    
      $imagick->clear();
      $imagick->destroy();
    } 
    
    $ /usr/bin/time -f %M:%e ../convert-imagick.php nipguide.pdf 
    nipguide.pdf has 58 pages
      rendering page 0 ...
    ...
      rendering page 57 ...
    255640:223.26
    
    $im->pingImage($tempFile);
    $nrOfPages = $im->getNumberImages();