为啥我的 PHP 脚本在生成缩略图时会停止?
Posted
技术标签:
【中文标题】为啥我的 PHP 脚本在生成缩略图时会停止?【英文标题】:Why is my PHP script stopping when generating thumbnails?为什么我的 PHP 脚本在生成缩略图时会停止? 【发布时间】:2021-08-21 20:09:14 【问题描述】:我是一名 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 等),这只会略微改变成功缩略图的数量。感谢您的任何想法,我有点迷路了。
【问题讨论】:
看起来服务器超时有问题。过程停止需要多长时间? @DavidPauli -- 感谢您的评论。加载时间变化很大。我测试了页面加载时间,得到:48s、123s、57s、115s。我很困惑! “脚本文件夹中的php.ini” 为什么那个文件会有影响? @Olivier - 感谢您的参与。正如我所说,我是一个新手,我理解(可能是错误的?)在那里放置一个 php.ini 文件可以让我修改一些参数(执行时间,内存限制等)你有什么建议吗?我渴望学习。 @Kerans 当 PHP 作为 CGI 运行时,您可以将.user.ini
文件放在目录中以覆盖设置(如 here 所述)。当 PHP 作为 Apache 模块运行时,只能使用 .htaccess
文件。在任何情况下,请始终致电phpinfo()
来检查配置。
【参考方案1】:
问题肯定是超时或资源稀缺(内存或类似的东西)。 This article 显示 ImageMagick 导致每个图像使用 315.03 MB 内存和 47% 的 CPU 使用率。如果您在foreach
循环中循环浏览处理器中的图像,这可能会复合/泄漏。图像转换在服务器资源上可能很昂贵。这也可能是您的某个图像文件或其他一些奇怪的问题。
Ajax
解决这些问题的方法是小批量处理这些问题。创建一个简单的 ajax/php 循环 - 为 ajax 使用一个 html.php 页面并为处理使用一个单独的文件,如下所示:
thumb-display.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 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>
thumb-process.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 发送每个文件路径(这样就不必每次都重新计算该路径中的所有文件),但我个人感觉更好通过帖子而不是图像路径发送数字。如果您希望发送大量文件路径而不是索引,请告诉我#。
【讨论】:
感谢 Kinglish,这听起来像是一个实用的解决方案。我没有得到的是 - 为什么在脚本停止之前根本没有输出?我怎样才能找到达到的限制(内存或其他)?我有一个 cPanel,如果有帮助的话。感谢您的宝贵时间! @Kerans - 你可以在你的 PHP 文件中初始化 error_reporting - ***.com/questions/1053424/… - 我知道 cpanel 过去也有查看错误日志的方法。我的解决方案的好处是它是可扩展的——可以处理 10,000 张图像,这很好。它比直接的 PHP 循环需要更长的时间,但在您的服务器上更容易并且更容易排除故障 @Kerans 我对 ImageMagik 做了更多的研究(见新的第一段),还编辑了我的脚本以允许您可以在 javascript 中设置的批处理(而不是一个一个)。欢呼【参考方案2】:@kinglish 的答案很适合通过 ajax 加载它们,但我认为这不是正确的方法。这减轻了服务器的一些处理,因为它不会一次全部运行它们,但如果您希望许多用户访问 195 张图像,它仍然不是一个好的解决方案
这里最大的问题是您试图根据请求处理图像,因此每个页面加载都会请求并重新创建所有 195 个图像。这对性能不利,并且您网站上的 10 位用户可能只需刷新几次页面就会使您的服务器崩溃,除非您为此支付超过 10 美元/月的费用。
所以,这里有一个更好的解决方案。
仅在 198 个图像的循环上运行图像处理脚本服务器端。 您可以查看如何运行命令来执行 php 文件,或者如果您通过 SSH 连接到 linux 机器,则只需执行“php yourfile.php”即可。
该脚本将比通过浏览器请求更快地运行和处理图像。 [不知道具体情况,就是这样]
将图像存储到“缩略图”目录,然后像往常一样通过标签加载它们。 TADA 问题解决了。您只需以这种方式处理一次,其余时间仅将它们提供给客户。 是的,您现在同时拥有原始大小和缩略图版本,这感觉不对,但执行起来比尝试在每个请求上重新生成它们要好得多。
添加一些缓存 [将它们加载到 S3,或使用 cloudflare 来缓存它们],您的网站将与任何网站一样快。
【讨论】:
以上是关于为啥我的 PHP 脚本在生成缩略图时会停止?的主要内容,如果未能解决你的问题,请参考以下文章
使用 C# System.Drawing 生成缩略图时居中裁剪图像