PHP允许图像上传旋转耗尽内存大小
我有一个脚本可以上传图片并根据方向旋转它们,我遇到的问题是当上传带有EXIF标签的图片时,我收到一个错误消息:
允许的内存大小33554432字节耗尽(试图分配10368字节。
然后在错误日志中提到它所在的行。
我确实注意到它只发生在具有EXIF标签的图像上。 如果正常的图像,由Photoshop或上传的东西上传,它没有任何问题。
实际的图像方向代码如下所示:
function correctImageOrientation($fullpath) {
if (function_exists('exif_read_data')) {
$exif = exif_read_data($fullpath);
if($exif && isset($exif['Orientation'])) {
$orientation = $exif['Orientation'];
if($orientation != 1){
$img = imagecreatefromjpeg($fullpath);
$deg = 0;
switch ($orientation) {
case 3:
$deg = 180;
break;
case 6:
$deg = 270;
break;
case 8:
$deg = 90;
break;
}
if ($deg) {
$img = imagerotate($img, $deg, 0);
}
// then rewrite the rotated image back to the disk as $filename
imagejpeg($img, $fullpath, 100);
} // if there is some rotation necessary
} // if have the exif orientation info
} // if function exists
}
在内存问题发生的error_log中的确切行实际上是它说:
$img = imagerotate($img, $deg, 0);
我在脚本中调用它的方式如下:
$dirname = session::value('user_id');
$rotatedfile = '/home/myfolder/public_html/'.$dirname.'/'.$file_name;
$rotatedfile = $this->correctImageOrientation($rotatedfile);
我基本上试图实现的是,旋转的图像被保存在原始文件的相同位置,基本上取代它。
再一次,这只发生在包含EXIF信息的图像上。 所有其他上传没有问题。
什么可能导致这种内存分配问题?
你的错误是这样的:
允许内存大小33554432字节耗尽(试图分配10368字节)。
33554432字节转换为32兆字节。 所以这都意味着PHP在尝试做一些工作时耗尽内存。
你声称失败的图像有EXIF信息,但这并不符合我的原因。 无论如何,快速解决您的问题是通过添加ini_set
连接到memory_limit
的函数来为您的函数增加PHP内存
例如,在执行if (function_exists('exif_read_data')) {
check之后,将其添加到此处。 我将它设置为64M
因为它在运行此功能时将有效地将脚本内存容量加倍。 代码在这里:
function correctImageOrientation($fullpath) {
if (function_exists('exif_read_data')) {
ini_set('memory_limit', '64M');
$exif = exif_read_data($fullpath);
if($exif && isset($exif['Orientation'])) {
$orientation = $exif['Orientation'];
if($orientation != 1){
$img = imagecreatefromjpeg($fullpath);
$deg = 0;
switch ($orientation) {
case 3:
$deg = 180;
break;
case 6:
$deg = 270;
break;
case 8:
$deg = 90;
break;
}
if ($deg) {
$img = imagerotate($img, $deg, 0);
}
// then rewrite the rotated image back to the disk as $filename
imagejpeg($img, $fullpath, 100);
} // if there is some rotation necessary
} // if have the exif orientation info
} // if function exists
}
我基本上试图实现的是,旋转的图像被保存在原始文件的相同位置,基本上取代它。
问题在于,您正在PHP中使用GD库,当PHP将文件加载到系统中时会占用内存,并在尝试旋转图像时吃掉更多内存。
这可能是因为具有EXIF信息的图像实际上具有比标准72dpi更高的DPI。 所以虽然它们的尺寸可能看起来与没有EXIF信息的其他图像相同,但300dpi图像的尺寸实际上比72dpi图像大4倍。 这很可能是为什么这些图像失败; 不是EXIF信息,而是整体DPI。
现在,您还可以通过更改将读为memory_limit = 32M
的行来更改php.ini
的内存限制。 从技术上讲,这是可行的。 但我认为这是一个失败的函数脚本的好习惯。
这是因为当你改变php.ini
的设置时,它会增加所有PHP交互的RAM; 不只是问题的问题。 所以你的服务器突然吃掉了更多的RAM(用于运行PHP的Apache)用于基本功能,以及古怪的功能,吃掉更多的RAM。 这意味着如果这些代码每天只能访问几次,那么为什么还要为每个PHP进程的32M内存感到满足的大型服务器负载呢? 更好地使用ini_set('memory_limit', '64M');
以隔离内存增加这样的需求。
imagecreatefromjpeg
解压缩图像并将结果存入内存。 这就是3 MB JPEG需要32 MB内存的原因(具体数量取决于分辨率,颜色深度等)。
你需要这个结果,所以你把它分配给一个变量:
$img = imagecreatefromjpeg($fullpath);
而现在的问题。 imagerotate
使用$img
资源,将其旋转并将结果放入内存的新区域,因为它通过设计返回新的图像资源,而不是覆盖给定的$img
资源。 所以最后你需要64 MB的内存:
$img = imagerotate($img, $deg, 0);
证明:
// our image
$url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Pluto-01_Stern_03_Pluto_Color_TXT.jpg/1024px-Pluto-01_Stern_03_Pluto_Color_TXT.jpg';
file_put_contents('../cache4/' . basename($url), fopen($url, 'r'));
$filename = '../cache4/' . basename($url);
echo 'Before imagecreate: ' . round(memory_get_usage() / pow(1024, 2)) . ' MB (Max: ' . round(memory_get_peak_usage() / pow(1024, 2)) . ' MB)<br>' . PHP_EOL;
// read image into RAM for further usage
$image = imagecreatefromjpeg($filename);
echo 'After imagecreate: ' . round(memory_get_usage() / pow(1024, 2)) . ' MB (Max: ' . round(memory_get_peak_usage() / pow(1024, 2)) . ' MB)<br>' . PHP_EOL;
// rotate image
$result = imagerotate($image, 180, 0);
echo 'After imagerotate: ' . round(memory_get_usage() / pow(1024, 2)) . ' MB (Max: ' . round(memory_get_peak_usage() / pow(1024, 2)) . ' MB)<br>' . PHP_EOL;
// flip image
imageflip($result, IMG_FLIP_VERTICAL);
echo 'After imageflip: ' . round(memory_get_usage() / pow(1024, 2)) . ' MB (Max: ' . round(memory_get_peak_usage() / pow(1024, 2)) . ' MB)<br>' . PHP_EOL;
返回:
Before imagecreate: 0 MB (Max: 0 MB)
After imagecreate: 5 MB (Max: 5 MB)
After imagerotate: 10 MB (Max: 10 MB)
After imageflip: 10 MB (Max: 10 MB)
正如你所看到的, imagerotate
被调用后峰值imagerotate
10 MB,但像imageflip
这样的函数不会显示这种行为,因为它会在内部覆盖变量。
也许你认为你可以通过覆盖这个变量来解决这个问题:
$image = imagerotate($image, 180, 0);
但是这只会减少当前的使用情况:
After imagerotate: 5 MB (Max: 10 MB)
很遗憾,自PHP 5.4以来,我们无法通过引用来传递变量:
imagerotate(&$image, 180, 0);
我打开了一个错误报告,以便将来有望优化。
链接地址: http://www.djcxy.com/p/61151.html上一篇: php allowed memory size exhausted on image upload rotation