首页 > 解决方案 > 为什么我的 PHP 脚本在生成缩略图时会停止?

问题描述

我是 PHP 初学者,我正在尝试构建一个图库页面,该页面将为文件夹中的所有 195 个图像输出缩略图。这些图像平均每个 8 MB。这是目前的情况:

脚本文件夹中的 php.ini

allow_url_fopen = On
display_errors = On
enable_dl = Off
file_uploads = On
max_execution_time = 999
max_input_time = 999
max_input_vars = 1000
memory_limit = 999M
post_max_size = 516M
session.gc_maxlifetime = 1440
session.save_path = "/var/cpanel/php/sessions/ea-php74"
upload_max_filesize = 512M
zlib.output_compression = Off

PHP/HTML 代码

<?php
DEFINE('UPLOAD_DIR', 'sources/');
DEFINE('THUMB_DIR', 'thumbs/');

function GenerateThumbnail($src, $dest)
{
    $Imagick = new Imagick($src);
    
    $bigWidth = $Imagick->getImageWidth();
    $bigHeight = $Imagick->getImageHeight();
    
    $scalingFactor = 230 / $bigWidth;
    $newheight = $bigHeight * $scalingFactor;
    
    $Imagick->thumbnailImage(230,$newheight,true,true);
    $Imagick->writeImage($dest);
    $Imagick->clear();
    
    return true;    
}

// Get list of files in upload dir
$arrImageFiles = scandir(UPLOAD_DIR);

// Remove non-images
$key = array_search('.', $arrImageFiles);
if ($key !== false) 
    unset($arrImageFiles[$key]);

$key = array_search('..', $arrImageFiles);
if ($key !== false) 
    unset($arrImageFiles[$key]);

$key = array_search('.ftpquota', $arrImageFiles);
if ($key !== false) 
    unset($arrImageFiles[$key]);

$key = array_search('thumbs', $arrImageFiles);
if ($key !== false) 
    unset($arrImageFiles[$key]);

?><!DOCTYPE HTML>
<html lang="fr">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Select Image</title>
</head>
    <body>
<?php
foreach($arrImageFiles as $imageFile)
{
        $thumbFullPath =  THUMB_DIR . "th_" . $imageFile;
        $imageFullPath = UPLOAD_DIR . $imageFile;

        if (! file_exists($thumbFullPath))  
        {           
            GenerateThumbnail($imageFullPath, $thumbFullPath);
        }
        
        echo "<img alt='' src='" . $thumbFullPath . "'>";
    
}
?>              
    </body>
</html>

我不知道如何解决的两个问题是:

  • 该脚本似乎在生成所有缩略图之前的某个时间点停止(在 30-50 个缩略图之后)。没有记录错误
  • 在脚本停止之前,页面不会加载到浏览器中,然后我可以看到(不完整的)输出
  • 这是我已经尝试过的:

  • 使用输出缓冲(可能因为新手不正确)
  • 将输出连接到一个变量中并在最后回显它(没有变化)
  • 使用不同的生成缩略图的方法(imagecopyresample、imagescale、ImageMagick 等)这只会略微改变成功缩略图的数量。
  • 感谢您的任何想法,我有点迷路了。

    标签: phphtmlimagemagickthumbnails

    解决方案


    问题肯定是超时或资源稀缺(内存或类似的东西)。本文显示 ImageMagick 导致每个图像使用 315.03 MB 内存和 47% 的 CPU 使用率。如果您循环浏览处理器中的图像,这可能会复合/泄漏foreach。图像转换在服务器资源上可能很昂贵。这也可能是您的一个图像文件或其他一些奇怪的问题。

    阿贾克斯

    解决任何这些问题的方法是小批量处理这些问题。创建一个简单的 ajax/php 循环 - 为 ajax 使用一个 html.php 页面并为处理使用一个单独的文件,如下所示:

    拇指显示.php

    <script>
    const batchSize = 5; // number of images per send
    function doThumb(nextIndex) {
        $.ajax({
          url: "thumb-process.php",
          method: "post",
          data: {nextIndex: nextIndex, batchSize: batchSize},
          dataType: "json"
        }).done(function(response) {
          if (response.thumbs) {
             response.thumbs.forEach( th => $('.thumbs').append('<img alt="" src="'+th+'" />') ) ;
          }
          if (response.finished) alert('finished')
          else {
            console.log('processed batch index #' + (nextIndex-batchSize + ' - ' + nextIndex);
            nextIndex += batchSize;
            setTimeout(() => { doThumb(nextIndex) }, 1000);
          }
        }).error(function (e) {
           console.error('Error happened on batch index #' + (nextIndex-batchSize + ' - ' + nextIndex, ' - details: ', e);
        })
        ;
    }
    
    $(document).ready(() => { doThumb(0); } )
    </script>
    
    
    <div class='thumb-list'></div>
    

    拇指进程.php

    function GenerateThumbnail($src, $dest)
    {
        $Imagick = new Imagick($src);
        $bigWidth = $Imagick->getImageWidth();
        $bigHeight = $Imagick->getImageHeight();
        $scalingFactor = 230 / $bigWidth;
        $newheight = $bigHeight * $scalingFactor;
        $Imagick->thumbnailImage(230,$newheight,true,true);
        $Imagick->writeImage($dest);
        $Imagick->clear();
        return true;    
    }
    
    // Get list of files in upload dir
    $arrImageFiles = scandir(UPLOAD_DIR);
    
    // Remove non-images
    $key = array_search('.', $arrImageFiles);
    if ($key !== false) 
        unset($arrImageFiles[$key]);
    
    $key = array_search('..', $arrImageFiles);
    if ($key !== false) 
        unset($arrImageFiles[$key]);
    
    $key = array_search('.ftpquota', $arrImageFiles);
    if ($key !== false) 
        unset($arrImageFiles[$key]);
    
    $key = array_search('thumbs', $arrImageFiles);
    if ($key !== false) 
        unset($arrImageFiles[$key]);
    $nextIndex = $_POST['nextIndex'];
    $max = min( ($nextIndex + $_POST['batchSize']), count($arrImageFiles)-1);
    $thumbs = [];
    while($nextIndex < $max){    
      $imageFile = $arrImageFiles[$nextIndex ];
      $thumbFullPath =  THUMB_DIR . "th_" . $imageFile;
      $imageFullPath = UPLOAD_DIR . $imageFile;
      if (!$imageFile) {
        $output = array('finished' => 1) ;
        die(json_encode($output));
      }
      if (! file_exists($thumbFullPath))  {           
        GenerateThumbnail($imageFullPath, $thumbFullPath);
      }
      $thumbs[]= $thumbFullPath ;
      $nextIndex++;
    }
    
    $output = array('thumbs' => $thumbs);
      
    if ($nextIndex >= count($arrImageFiles)-1) $output['finished'] = 1 ;
    
    echo json_encode($output);
    

    ..然后坐下来看看你的控制台。如果您遇到超时或其他错误,您会看到nextIndex它被阻塞,您可以使用该数字作为起始索引而不是 0 重新加载您的页面。

    当然,您可以在 thumb-display.php 中将所有文件路径收集在一个数组中,并通过 ajax 发送每个文件路径(这样就不必每次都重新计算该路径中的所有文件),但我个人觉得发送一个数字会更好通过帖子而不是图像路径。如果您希望发送大量文件路径而不是索引#,请告诉我。


    推荐阅读