2017-08-18 47 views
0

正如人们已经发现的,当试图将文件加载到Imagick中,无论出于何种原因都无法处理时,它会抛出一个无法捕捉的异常。使用imagick处理未知图像

我创建使用readImageFile ICO文件,并试图加载它():

$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); 

使用,而不是readImageFile READFILE,这显然是着眼于扩展,因为这个代码也运行正常:

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

然而,当我重命名ICO文件图标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()函数不会采用的。

什么是最好的方式去做这件事?

回答

0

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

正如您已经正确发现的那样,从文件描述符中读取图像不允许ImageMagick回退文件扩展名,因此您在致电Imagick::readImageFile之前负责告知Imagick该格式。

What would be the best way to go about this?

你必须说明什么是允许的。内置的FileInfo方法是一个很好的开始(但不是简单的证明)。但是,如果你已经是一个文件描述符开放的,我会建议“偷看”数据...

$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人,可以帮助。

+0

不错,谢谢你的回复!不幸的是,这不仅仅是我需要允许的ICO文件,而是一大堆。我说服客户撰写白名单。我希望finfo_buffer足够安全。维护我自己的一大堆文件类型的4字节签名列表似乎并不理想。我只是希望Imagick不会因为无法检测到正确的文件类型而出现如此惊人的失败。 –

+0

这可能是重新评估解决方案体系结构的时候了。您应该努力将原始文件名传递给'Imagick'。提升异常也很常见,所以应用程序应该能够期待和处理这种情况。 – emcconville

+0

我不确定我可以信任原始文件名,因为我不信任我的用户。正如我从这个问题所理解的那样,当Imagemagick抛出一个致命的错误时,你无法做到这一点:https://stackoverflow.com/questions/28156447/how-do-i-catch-an-imagick-fatal -error-in-php –