DEVELOPERS BLOGデベロッパーズブログ

  1. HOME > 
  2. 加藤 正人のデベロッパーズブログ > 
  3. UploadPack を Exif Orientation 対応にする

加藤 正人のデベロッパーズブログ

加藤 正人

氏名
加藤 正人
役職
多分SE
血液型
秘密
出没
美味しいもののあるところ
特色
タヒチ大好き。ちょいメタボ。

加藤 正人

2014/07/03

UploadPack を Exif Orientation 対応にする

 

CakePHP での開発に Michał Szajbe 氏の UploadPack をよく使っているのだが、今回 UploadPack を使用したサイトで JPEG 画像の Exif Orientation 対応が必要となった。

UploadPack では、アップロードされたファイルは UploadBehavior::_writeFiles() メソッド内でアトミックに処理されている。このため、アップロード画像を画像スタイルに応じてリサイズする前に Exif Orientation 処理をするにはこの _writeFiles() メソッドに修正を加えざるを得ない。

可能な限りオリジナルのコードに手を加えずに対応するため、今回は「コールバック」メソッドを追加する方法を採用した。

  1. UploadBehavior の _writeFiles() プライベートメソッド内 (下記コードの13行目あたり) に、コールバックメソッドの呼び出しとして「$this->afterMove($settings['path']);」という1行を追加する。
                      private function _writeFiles(&$model) {
                        if (!empty($this->toWrite)) {
                          foreach ($this->toWrite as $field => $toWrite) {
                            $settings = $this->_interpolate($model, $field, $toWrite['name'], 'original');
                            $destDir = dirname($settings['path']);
                            if (!file_exists($destDir)) {
                              @mkdir($destDir, 0777, true);
                              @chmod($destDir, 0777);
                            }
                            if (is_dir($destDir) && is_writable($destDir)) {
                              $move = !empty($toWrite['remote']) ? 'rename' : 'move_uploaded_file';
                              if (@$move($toWrite['tmp_name'], $settings['path'])) {
                                $this->afterMove($settings['path']);	// ←この行を追加
                                if($this->maxWidthSize) {
                                  $this->_resize($settings['path'], $settings['path'], $this->maxWidthSize.'w', $settings['quality'], $settings['alpha']);
                                }
                                foreach ($settings['styles'] as $style => $geometry) {
                                  $newSettings = $this->_interpolate($model, $field, $toWrite['name'], $style);
                                  $this->_resize($settings['path'], $newSettings['path'], $geometry, $settings['quality'], $settings['alpha']);
                                }
                              }
                            }
                          }
                        }
                      }
    
  2. 続いて、protected メソッド afterMove を UplaodBehavior 内に定義する。
    		  protected function afterMove($file)	// 画像移動 (リネーム) 後のコールバック処理
    		  {
    		    // 既定値は何もしない
    		  }
    
    以上が UploadBehavior.php 内の修正の全てだ。
  3. 次に、UploadBehavior を継承した自分専用クラス MyUploadBehavior を定義する。今回は{$APP}/Model/Behavior に MyUploadBehavior.php として配置した。この新しいクラスで afterMove() コールバックメソッドをオーバーライドすることで、アップロードした画像ファイルをリサイズする前に適用すべき処理を、オリジナルの UploadBehavior とは切り離して記述することができる。
    App::uses('UploadBehavior', 'UploadPack.Model/Behavior');
    
    class MyUploadBehavior extends UploadBehavior
    {
      protected function afterMove($file)	// 画像移動 (リネーム) 後処理コールバック
      {
        if ( ($exif=@exif_read_data($file)) && ($orientation=@$exif['Orientation']) )
        {
          // Exif 情報が存在しており、Orientation が指定されていた
          $tmpfile = tempnam(TMP, '_');
          $convert = '/usr/bin/convert';	// ImageMagick の convert コマンドへのパス
          switch ( (int)$orientation )
          {
          case 1: // そのまま
            break;
          case 2:	// 左右反転 = convert -flip {src} {dst}
            // …【中略】…
            break;
          case 3: // 180°回転 = convert -rotate 180 {src} {dst}
            // …【中略】…
            break;
          case 4: // 上下反転 = convert -flop {src} {dst}
            // …【中略】…
            break;
          case 5: // 90°右回転+左右反転 convert -rotate 90 {src} - | convert -flip - {dst}
            // …【中略】…
            break;
          case 6: // 90°右回転 = convert -rotate 90 {src} {dst}
            system("{$convert} -rotate 90 {$file} {$tmpfile}; mv {$tmpfile} {$file}");
            break;
          case 7: // 90°左回転+左右反転 = convert -rotate -90 {src} - | convert -flip - {dst}
            // …【中略】…
            break;
          case 8: // 90°左回転 = convert -rotate -90 {src} {dst}
            // …【中略】…
            break;
          }
        }
      }
    }
    
    なお、上記では画像の処理を system 関数経由で ImageMagick の convert コマンドで処理しているが、利用環境に応じて GD あるいは Imagick ライブラリで処理するよう記述することも可能である。
  4. 最後に、画像処理が必要なモデルから MyUploadBehavior を使用するよう記述する。
    App::uses('AppModel', 'Model');
    App::uses('MyUploadBehavior', 'Model/Behavior');
    
    class Member extends AppModel
    {
      public function $actsAs = array(
        'MyUpload' => array(
          // UploadPack の UploadBehavior と同じオプションを記述
        ),
      );
      // …【以下略】…
    }
    

この方法を使うことで、それぞれの動作環境に応じた手段で、画像リサイズに先立つ Exif 処理のほかウォーターマーク追加などを UploadBehavior から切り離して対応することが可能となる。

2014/08/18 追記: この機能を作者の Michał Szajbe 氏に提案したところ、ver. 0.7.1 に取り込んでもらえた。

関連タグ: PHP5.3  UploadPack  CakePHP2 

関連エントリー

CakePHP のレンダリング結果を保存したい

CakePHP 2.x の Cookie と js.cookie.js

時刻入力用 jQuery Plugin TimePicki の不具合調整

CakePHP プラグインで HTTPS 判定

作業用モデルビヘイビア

CakePHP のバリデーション時に状況に応じてバリデーションルールを変更する

CsvLoader ビヘイビアを使って CSV を一発でロードする Cake Shell

無名関数からの外部変数参照について

CakePHP の Security コンポーネント、ベーシック認証、CGI

形態素解析システム MeCab (めかぶ) を CakePHP から利用する

Google APIs Client Library を CakePHP から利用する

分数電卓を作ってみる・その2

分数電卓を作ってみる・その1

CsvLoaderBhavior - CSV データを CakePHP で扱うための一手法・その2

 CsvLoaderBhavior - CSV データを CakePHP で扱うための一手法

CakePHP でストアドファンクション

テキストフィールドを CSV として処理する CakePHP ビヘイビア・補足

テキストフィールドを CSV として処理する CakePHP ビヘイビア・その2

テキストフィールドを CSV として処理する CakePHP ビヘイビア・その1

Google Maps で郵便番号⇒住所変換・その2

Google Maps で郵便番号⇒住所変換

テキストを自動整形する簡易 CakePHP ヘルパー

Google Chart へのアクセスをヘルパーで実装してみる【その2】

Google Chart へのアクセスをヘルパーで実装してみる【その1】

Google reCAPTCHA を使いやすくする

駅データ.jp のデータを CakePHP から使う

CakePHP の小ネタ

作業用テーブル処理を再利用可能なツール化してみる・その1

クリックイベントが2回実行される?!

CakePHP のフォームで required・その2

CakePHP の Paginator の sort() で昇順ソート時に NULL 値を最後にしたい

CakePHP のフォームで required

UploadPack のヘルパーメソッド名称が変わっていた

CakePHP 2.x の PaginatorHelper::sort の不思議

UploadPack で四苦八苦・再び

CakePHP で四苦八苦 - あるフィールドにだけ値が書き込まれない

UploadPack で四苦Hack その後

CakePHP の小ネタ

CakePHP v2 のエラー処理

IE10 と CKEditor

2013/03/23
IE10 と CKEditor

UploadPack で四苦Hack・その2

Paginator で見かけの URL を使用する方法

「Notes プラグインが見つかりません」

PHP 5.3 の「無名関数」

CakePHP v2 で HTTP エラー画面をカスタマイズする

UploadPack で四苦Hack

PHP 5.4 と CakePHP 2.x