dari88's diary

これから趣味にするプログラミング/PHP/javascript/kohana/CMS/web design/

kohanaのテスト12-10・・・写真のアップロード機能を作る

 kohana の WordPress 化計画で kohana のテストを進めています。今日のお題は写真のアップロード機能です。アップロードには jQuery プラグインの Uploadify を使います。アップロードした写真の処理には ImageMagick を使います。WordPress は写真をフォルダに保管していますが、気に入らないのでデータベースにに保管することにします。尚、前回の日記に書きましたが、大きなデータを扱いますので MySQLSQL 文の最大値の設定変更を忘れずに実施しましょう。

 

写真をアップロードする・・・Uploadifyを使う

 写真のアップロードには4月4日の日記で導入した Uploadify を使います。

<準備作業>
xampp/htdocs/includes/uploadify/ 下に Uploadify のファイル群を配置します。
・写真処理用に htdocs/uploads/ フォルダを作っておきます。
css と js のオートロード用に下記を定義ファイルに追加します。

・kohana/application/config/wp-js-css.php に追加

'jquery-142min' => '/includes/uploadify/jquery-1.4.2.min.js',
'swfobject-uploadify' => '/includes/uploadify/swfobject.js',
'jquery-uploadify-v214min' => '/includes/uploadify/jquery.uploadify.v2.1.4.min.js',
        
'uploadify' => '/includes/uploadify/uploadify.css',

  次に4月4日に使った Uploadify 用のページを改造してビューを作ります。

・kohana/application/views/test12/postnew/uploadify.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link href="/kohana/loadcss?c=1&load=uploadify" rel="stylesheet" type="text/css" />
        <script type="text/javascript" src="/kohana/loadjs?c=1&load=jquery-142min,swfobject-uploadify,jquery-uploadify-v214min"></script>
        <script type="text/javascript">
            // <![CDATA[
            $(document).ready(function() {
                $('#file_upload').uploadify({
                    'uploader'  : '/includes/uploadify/uploadify.swf',
                    'script'    : '/kohana/test12_uploadify',
                    'cancelImg' : '/includes/uploadify/cancel.png',
                    'folder'    : <?php echo "'/".$folder."'" ?>,
                    'ID'        : '1',
                    'queueID'   : 'queue',
                    'fileDesc'  : '',
                    'sizeLimit' : 10240000,
                    'multi'     : true,
                    'auto'      : false
                });
            });
            // ]]>
        </script>
        <title>Uploadifyを使って複数の画像をアップロードする</title>
    </head>
    <body>
        <a>ファイルをアップロードして下さい</a>
        <div style="background-color: #505050; height: 200px; margin-bottom: 10px; 
             padding:10px; overflow: auto; width: 400px;" id="queue"></div>
        <input type="file" id="file_upload" name="file_upload" /><br />
        <a href="javascript:$('#file_upload').uploadifyUpload();">アップロードする</a>
    </body>
</html>

 スクリプトの 'folder' の所に $folder を書き込んでいます。ここには後ほどコントローラからログインユーザー名を渡します。アップロードは javscript によってリクエストされます。するとリクエストされたコントローラ test12_uploadify はセッション情報を利用できないので、ユーザー名が分かりません。しようがないので Uploadify に渡してもらうことにしました。

 このビューを表示するコントローラは単純です。

・kohana/application/classes/controller/test12/mediaupload.php

<?php defined('SYSPATH') OR die('No direct access allowed.');

class Controller_Test12_mediaupload extends Controller {

    public function action_index() {

        $loginuser = Auth_Wplogin::instance()->get_user();
        if (!$loginuser)
            $this->request->redirect('test12');

        $view = view::factory('test12/postnew/uploadify');
        $view->folder = $loginuser;
        $this->response->body($view);
    }

}

?>

 ビューにログインユーザー名を渡しています。

 

ImageMagick を使う

 次は Uploadify がリクエストを送るコントローラです。4月6日の日記で導入した ImageMagick を使います。

・kohana/application/classes/controller/test12/uploadify.php

<?php defined('SYSPATH') OR die('No direct access allowed.');

class Controller_Test12_uploadify extends Controller {

    public function action_index() {

        try {
            if (empty($_FILES)) {
                throw new Exception('Invalid access!');
            }

            $filename = $_FILES['Filedata']['name'];
            $fileParts = pathinfo($filename);
            $ext = strtolower($fileParts['extension']);
            $typesArray = array('jpg', 'gif', 'png');

            if (!in_array($ext, $typesArray)) {
                throw new Exception('Invalid file type!');
            }

            $timestamp = time();
            $tempFile = $_FILES['Filedata']['tmp_name'];
            $targetPath = $_SERVER['DOCUMENT_ROOT'] . '/uploads/';
            $targetPath = str_replace('//', '/', $targetPath);
            $uniq_filename = $timestamp . '_' . Auth_Wplogin::generate_password(4);
            $targetFile = $targetPath . $uniq_filename . '.' . $ext;
            $thumbnail_1 = $targetPath . $uniq_filename . '_320x.' . $ext;
            $thumbnail_2 = $targetPath . $uniq_filename . '_1024x.' . $ext;

            $imagick = new Imagick($tempFile);
            $size = $imagick->getImageLength();
            if ($size > 10250000) {
                throw new Exception('File size over!');
            }
            $width = $imagick->getImageWidth();
            $height = $imagick->getImageHeight();
            $imagick->writeImage($targetFile);
            if ($width > 1024 or $height > 1024) {
                $imagick->thumbnailImage(1024, 1024, TRUE);
                $imagick->writeImage($thumbnail_2);
            } else {
                $imagick->writeImage($thumbnail_2);
            }
            $imagick->thumbnailImage(320, 320, TRUE);
            $imagick->writeImage($thumbnail_1);
            $imagick->Destroy();

            $loginuser = substr($_REQUEST['folder'], 1);
            $user_ID = Auth_Wplogin::instance()->user_ID($loginuser);
            if (!$loginuser) {
                throw new Exception('Login!');
            }
            $tn1_img = file_get_contents($thumbnail_1);
            $tn2_img = file_get_contents($thumbnail_2);
            $org_img = file_get_contents($targetFile);

            $post_array = array(
                'post_author' => $user_ID,
                'extention' => $ext,
                'org_name' => $filename,
                'uq_name' => $uniq_filename . '.' . $ext,
                'tn1_name' => $uniq_filename . '_320x.' . $ext,
                'tn2_name' => $uniq_filename . '_1024x.' . $ext,
                'tn1_img' => $tn1_img,
                'tn2_img' => $tn2_img,
                'org_img' => $org_img,
            );

            $model = Model::factory('test12_posts');
            $model->postnewimages($post_array);

            unlink($thumbnail_1);
            unlink($thumbnail_2);
            unlink($targetFile);

            echo str_replace($_SERVER['DOCUMENT_ROOT'], '', $targetFile);
        } catch (Exception $e) {
            $log = print_r($e->getMessage(), TRUE) . "\n";
            $logfile = $targetPath . 'log.txt';
            file_put_contents($logfile, $log, FILE_APPEND | LOCK_EX);
            throw new Exception($e->getMessage());
            die('Invalid');
        }
    }

}

?>

  今日のキモはこのコントローラです。いろいろな事をやってます。

・try ~ catch 文を使ってエラーを制御して Uploadify に返しています。
・エラーが発生した場合、ログを取るようにしています。
・悪意のあるユーザーはどんなファイルを送ってくるか分かりません。ImageMagick がエラーを返すようなファイルは壊れているか悪意のあるファイルと判定します。
・写真には一意の名前を発生させています。
・サムネールは大と小の2種類を作っています。
・写真はデータベースに保管し、一時ファイルは消去します。

 因みに、WordPress は漢字のファイル名を持つ画像に対してアップロードの記録は残すものの画像そのものは記録されません。理解不能な仕様です。今回作成したコードは日本語はもちろんのこと、中国語のファイル名であっても正しく処理されます。

 

写真をデータベースにバイナリーのまま保存する

 次はモデルを書きます。その前にデータベースのテーブルが必要です。phpMyAdmin を使って kohana データベースにテーブルを作ります。

CREATE TABLE IF NOT EXISTS `wp332_post_images` (
  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `post_author` bigint(20) unsigned NOT NULL DEFAULT '0',
  `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_date_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `extention` varchar(10) NOT NULL DEFAULT '',
  `org_name` varchar(200) NOT NULL DEFAULT '',
  `uq_name` varchar(200) NOT NULL DEFAULT '',
  `tn1_name` varchar(200) NOT NULL DEFAULT '',
  `tn2_name` varchar(200) NOT NULL DEFAULT '',
  `title` varchar(200) NOT NULL DEFAULT '',
  `description` text NOT NULL,
  `tn1_img` MEDIUMBLOB NOT NULL,
  `tn2_img` MEDIUMBLOB NOT NULL,
  `org_img` MEDIUMBLOB NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `post_author` (`post_author`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

 次はモデルです。

・kohana/application/classes/model/test12/posts.php に追加します。

    public function postnewimages($array) {

        $date = Date::formatted_time();
        $post = array(
            'ID' => '',
            'post_author' => '0',
            'post_date' => $date,
            'post_date_gmt' => $date,
            'extention' => '',
            'org_name' => '',
            'uq_name' => '',
            'tn1_name' => '',
            'tn2_name' => '',
            'title' => '',
            'description' => '',
            'tn1_img' => '',
            'tn2_img' => '',
            'org_img' => '',
        );

        foreach ($array as $key => $value) {
            $post[$key] = $value;
        }

        $id = DB::insert(array_keys($post))
                ->values($post)
                ->table('wp332_post_images')
                ->execute();

        return $id;
    }

  MySQL 用のエスケープ処理は kohana に任せます。自分でエスケープすると2重処理になってしまい、写真データが壊れます。

 

動作確認

 ログイン状態でないと処理してくれないので、先ずはログインします。新規投稿画面の アップロード/挿入 をクリックすると jQuery Tickbox のポップアップの中にアップロード画面が登場するはずです。あるいは localhost/kohana/test12_mediaupload にアクセスします。

 画像が保存されたかどうか phpMyAdmin で確認しましょう。phpMyAdmin は画像データの部分のリンクをクリックすると何と写真をダウンロードしてくれるではありませんか! これには驚きです・・・(^^;;;

 保存した写真を表示するコードは次回ということで。