2017-08-18 88 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 –