imagecreatefromjpeg()で「marker 0xd9」エラーが出る問題について

PHPでユーザーからの画像ファイル投稿を受け付ける場合に、GDライブラリを利用したimagecreatefromjpeg()関数を使用すると、まれに以下のようなエラーが出てしまうことがあります。

Warning: imagecreatefromjpeg() [function.imagecreatefromjpeg]:
gd-jpeg, libjpeg: recoverable error: Corrupt JPEG data: 233
extraneous bytes before marker 0xd9 in /***.php on line 94
Warning: imagecreatefromjpeg() [function.imagecreatefromjpeg]:
'/***.jpg' is not a valid JPEG file in /***.php on line 94

赤い数字の箇所は画像ファイルによって異なります。

要するに「画像ファイル中の0xd9マーカーより前の233バイト部分にゴミ情報がありますよ。」ということでJPEG画像が壊れてるらしいです。

しかし、この画像は画像ビューワーで見ると問題なく見られます。
おそらくメタデータ(EXIF等)部分をGDがチェックして独自判断を下しているのでしょう。

このエラーが発生した画像をPhotoshopの「ファイル情報」を調べてみると「Sumsung GALAXY S II SC-02C」で撮影された画像ファイルでした。

こうなるとGDライブラリではどうしようもないので、手動でやるのであれば一度ローカルPCにダウンロードしてPhotoshop等の編集ツールで開いて新規画像ファイルにコピペして再アップロードすることになります。

どうしてもサーバー上で解決したい!

jheadライブラリを導入

こういう場合はEXIF情報を除去してくれる「jhead」ライブラリを利用しましょう。
ただし、通常のLinuxサーバーではデフォルトでインストールされていないので、yumコマンドでインストールします。

yumコマンドでjheadパッケージが見つからない場合は、epelリポジトリを追加するとインストールできますよ。サーバーOSのバージョンとCPUの種類でインストールするパッケージが異なるので注意。

#yum install jhead
...
Total download size: 54 k
Is this ok [y/N]: (yキー+RETURNキー)
...
Importing GPG key 0x217521F6 "Fedora EPEL <epel@fedoraproject.org>" from /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL
Is this ok [y/N]: (yキー+RETURNキー)
...
Installed: jhead.i386 0:2.90-2.el5
Complete!

PHPでWarning!を例外処理できるように変更

PHPでは通常「imagecreatefromjpeg()」で発生する警告エラー(Warning)を例外処理することはできませんが、set_error_handler()で独自関数を定義することで例外処理することができます。

/**
* 画像処理エラーを扱う関数
*
* @param integer $errno      エラー番号
* @param string  $errstr     エラーメッセージ
* @param string  $errfile    エラーが発生したファイル名
* @param integer $errline    エラーが発生した行番号
* @param array   $errcontext エラーコンテキスト
*/
function imageErrorHandler($errno, $errstr, $errfile, $errline, array $errcontext) {
    if (error_reporting()===0) {
        return false;
    }
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}

この関数を以下のように「try~catch」の前に設定しておきます。

/**
* メイン処理
*/
$file_path = './corrupted_image.jpg';
//エラー処理系を一時的に変更
set_error_handler("imageErrorHandler");
try {
    $img_rsrc = imagecreatefromjpeg($file_path);
} catch(ErrorException $e) {
    //0xd9のエラー
    if (strpos($e->getMessage(), '0xd9')!==false) {
        //エラーが起こった際の処理系をここに書きます。
    } else {
        //その他のエラー処理系をここに書きます。
    }
}
//エラー処理系を戻す
restore_error_handler();

エラーが発生したらjheadでEXIF情報を除去する

0xd9のバイトエラーが発生した場合、以下のような処理でEXIFデータを除去した画像ファイルに入れ替えることができます。

//0xd9のエラー
if (strpos($e->getMessage(), '0xd9')!==false) {
    $cmd = escapeshellcmd("jhead -purejpg {$file_path}");
    exec($cmd, $output);
    ...
    (再度imagecreatefromjpeg()等を実行する)
}

これでエラーが発生しても問題なく変換できるようになります。
お役に立ちましたでしょうか?

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

2013-02-20