【问题标题】:Handling unknown images with imagick用imagick处理未知图像
【发布时间】:2017-08-18 10:40:51
【问题描述】:

正如人们已经发现的那样,当尝试将一个文件加载到 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() 函数不会接受。

最好的方法是什么?

【问题讨论】:

    标签: php imagemagick imagick


    【解决方案1】:

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

    正如您正确发现的那样,从文件描述符读取图像不允许 ImageMagick 回退到文件扩展名,因此您有责任在调用 Imagick::readImageFile 之前告诉 Imagick 关于格式.

    最好的方法是什么?

    您必须明确允许的内容。内置的 FileInfo 方法 mime_content_typefinfo_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 人员可以提供帮助。

    【讨论】:

    • 很好,感谢您的回复!不幸的是,我需要允许的不仅仅是 ICO 文件,而是一大堆。我说服客户编写白名单。我希望 finfo_buffer 足够万无一失。为一大堆文件类型维护我自己的 4 字节签名列表似乎并不理想。我只是希望 Imagick 在无法检测到正确的文件类型时不会失败。
    • 可能是时候重新评估解决方案架构了。您应该努力将原始文件名传递给Imagick。引发异常也很常见,因此应用程序应该能够预期并处理此类情况。
    • 我不确定我是否可以信任原始文件名,因为我不信任我的用户。正如我从这个问题中了解到的那样,当 Imagemagick 引发致命错误时,您无能为力:stackoverflow.com/questions/28156447/…
    • 试试看。致命错误是没有捕获和处理异常。
    • 感谢您对我的包容,但我还是不明白。 Imagemagick 抛出一个致命错误。我可以抓住它,它会执行我放在 catch 块中的所有内容,但之后就结束了。无需处理更多图像,也无需渲染漂亮的 json 输出。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 1970-01-01
    • 2013-08-03
    • 2023-04-09
    • 2022-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多