用想象力处理未知图像

时间:2017-08-18 10:40:52

标签: php imagemagick imagick

正如人们已经发现的那样,当试图将文件加载到Imagick中时,无论出于何种原因它都无法处理,它将抛出一个无法捕获的异常。

我创建了一个ICO文件并尝试使用readImageFile()加载它:

$image = new imagick();
$handle = fopen('icon.ico', 'rb');
$image->readImageFile($handle);

这将抛出:

PHP Fatal error:  Uncaught exception 'ImagickException' with message 'no decode delegate for this image format `' @ error/constitute.c/ReadImage/501'

当我指定图像将是ICO文件时,它可以工作。所以这段代码运行良好:

$image = new imagick();
$image->setFormat('ICO');
$handle = fopen('icon.ico', 'rb');
$image->readImageFile($handle);

使用readFile而不是readImageFile,它显然会查看扩展名,因为这段代码运行正常:

$image = new imagick();
$image->readimage('icon.ico');

但是,当我将ICO文件重命名为icon.jpg并运行时:

$image = new imagick();
$image->readimage('icon.jpg');

失败并出现以下错误:

PHP Fatal error:  Uncaught exception 'ImagickException' with message 'Not a JPEG file: starts with 0x00 0x00 `icon.jpg' @ error/jpeg.c/JPEGErrorHandler/322'

但是,以下代码处理重命名为icon.jpg的ICO文件:

$image = new imagick();
$image->setFormat('ICO');
$handle = fopen('icon.jpg', 'rb');
$image->readImageFile($handle);

显然,处理图像的最佳方法是不要查看扩展名,这可能是任何内容,但请查看实际文件。 Imagick显然没有做到这一点。我们有PHP函数,如mime_content_type(),getimagesize()和finfo_buffer()(PHP.net建议我认为)。但是它们将返回类似“image / x-icon”的东西,setFormat()函数不会这样做。

最好的方法是什么?

1 个答案:

答案 0 :(得分:0)

确实,您无法信任文件名,但某些文件格式没有唯一的魔术文件签名,或者具有模糊的标识符。当ImageMagick读取图像时,它会尝试信任魔术签名FIRST,如果无法解析图像类型,则会回退到文件扩展名。

正如您所发现的那样,从文件描述符中读取图片并不允许ImageMagick回退到文件扩展名,因此您需要负责告诉Imagick关于调用Imagick::readImageFile之前的格式。

  

最好的方法是什么?

你必须明白允许的内容。内置的FileInfo方法mime_content_type& finfo_buffer是一个好的开始(但不是万无一失)。但是如果您已经打开了文件描述符,我会建议"偷看"在数据......

$filename = 'untrusted';
$handle = open($filename, 'rb');
// Allow the most common & modern formats to work as expected.
try {
   $image = new Imagick();
   // Pass original file name for IM fall-back
   $image->setFilename($filename);
   $image->readImageFile($handle);
// Attempt to handle specific formats.
} catch (ImagickException $e) {
   // Create new instance
   $image = new Imagick();
   // Rewind FD
   fseek($handle, 0);
   // Read first four bytes
   $peek = fread($handle, 4); 
   // Collect MIME-TYPE
   $mime = mime_content_type($filename); 
   // Explicitly allow KNOWN formats. (also see http://www.garykessler.net/library/file_sigs.html)
   if ($mime == 'image/x-icon' && $peek == "\x00\x00\x01\x00") {
       $image->setFormat('ICO');
   } else if ($mime == 'image/x-icon' && $peek == "\x00\x00\x02\x00") {
       $image->setFormat('CUR');
   } else {
     // Error handle
   }
   fseek($handle, 0); // Rewind again.
   $image->readImageFile($handle);
}

YMMV,我确信有更好的PHP人员可以提供帮助。