CodeIgniterでのファイルアップロードのラッパークラスを作った

CodeIgniterでサイト上で画像をアップする時に、通常は1ファイルのみの扱いですが、これを以前MYNETS_Uploadクラスというもので
複数ファイルの同時アップロードを行えるようにしました。

が、実際にファイルを操作するとなると、次のことが必要となります。
1)ファイルをリサイズする
2)複数サイズの画像をあらかじめ作成しておく
等が必要となります。

この場合、毎回同じような設定情報を受け渡したりして結構記述が面倒になります。
これを解消するため、MYNETS_Image_libクラスを作成しました。
基本的な動作はCI_Image_libをextendsし、あくまでもめんどくさい部分を解消するためのラッパークラスとして使います。

ソースはこちら

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 *
 * @category
 * @package    MYNETS_Image_lib Class
 * @author     KUNIHARU Tsujioka <kunitsuji@gmail.com>
 * @copyright  Copyright (c) 2008 KUNIHARU Tsujioka <kunitsuji@gmail.com>
 * @copyright  Copyright (c) 2006-2008 Usagi Project (URL:http://usagi-project.org)
 * @license    New BSD License
 */

// ------------------------------------------------------------------------

class MYNETS_Image_lib extends CI_Image_lib {

    private $CI;

    private $upload_base   = 'uploads/';
    private $max_size      = '500';        //500KByteまで
    private $max_width     = '1024';      //1024px
    private $max_height    = '1024';      //1024px
    private $overwrite     = TRUE;        //上書きするか
    private $allowed_types = 'gif|jpg|png';

    public  $upload_error_message = ''; //エラーがあった場合、エラーメッセージを格納する

    private $old_filename = '';       //オリジナルのファイル名
    private $new_filename = '';       //生成された新しいファイル名
    
    public  $upload_filename = array();  //アップロードされたファイル名を格納する配列
    /**
     * Constructor
     *
     * @access  public
     * @param   string
     * @return  void
     */
    public function __construct($params = array())
    {
        parent::__construct();

        if (count($params) > 0)
        {
            $this->initialize($params);
        }

        //file upload classをロード
        $this->CI =& get_instance();

        //log_message('debug', "Image Lib Class Initialized");
    }

    // --------------------------------------------------------------------

    /**
     * ファイルアップロードクラスをロードする
     * 必須はupload_path モジュール名以下、命名規則に沿って指定
     * 例)日記の場合は、'diary/XXXXX/XXXXX'など
     *
     */
    public function execute($config = array())
    {
        if (isset($config['upload_path']))
        {
            $config['upload_path'] = PUBPATH.$this->upload_base.$config['upload_path'];
            if ( ! $this->_path($config['upload_path']))
            {
                return FALSE;
            }
        }
        else
        {
            //パスの指定がないものはエラーとする
            $this->upload_error_message = 'ファイルを格納する場所が未定義です。';
            return FALSE;
        }
        
        if ( ! isset($config['allowed_types']))
        {
            $config['allowed_types'] = $this->allowed_types;
        }

        if ( ! isset($config['max_size']))
        {
            $config['max_size']      = $this->max_size;
        }

        if ( ! isset($config['max_width']))
        {
            $config['max_width']     = $this->max_width;
        }

        if ( ! isset($config['max_height']))
        {
            $config['max_height']    = $this->max_height;
        }

        if ( ! isset($config['overwrite']))
        {
            $config['overwrite']     = $this->overwrite;
        }

        $this->CI->load->library('upload', $config);
    }
    
    /**
     * 指定のフィールド名のファイルがアップロードされているかどうかを返却
     * @params  string  field name
     * @params  bool    TRUE or FALSE
     *
     */
    public function is_upload($name)
    {
        if ($this->CI->upload->is_uploaded($name))
        {
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }

    /**
     * 保存ディレクトリが存在しているかを調査する
     *
     *
     */
    private function _path($path)
    {
        if (is_dir($path))
        {
            if (is_writable($path) !== TRUE)
            {
                $this->upload_error_message = 'ファイルを書き込む権限がありません。';
                return FALSE;
            }
            else
            {
                return TRUE;
            }
        }
        else
        {
            //存在していない場合は、その場所を作成する
            if (mkdir($path,0777,TRUE))
            {
                return TRUE;
            }
            $this->upload_error_message = 'ファイルを格納する場所が作成できません。';
            return FALSE;
        }
    }

    /**
     * 画像を複数枚にコンバートする。変換する画像のサイズを配列で受け取る。
     * array('120', 180', '76',50)の場合、それぞれのサイズにリサイズする
     * オリジナル画像を保存する場合は、リネームして新しく保存する
     * @params  string field name
     * @params  array  convert size
     * @params  bool   original image deleted default FALSE
     * @params  bool   original image renamed default TRUE
     *
     */
    public function convert_image($column, $option, $deleted = FALSE, $rename = TRUE)
    {
        if ( ! $column)
        {
            return FALSE;
        }
        if ( ! $option)
        {
            return '';      //何もしない
        }

        if ( ! is_array($option))
        {
            $option = (array)$option;
        }

        $this->new_filename = '';
        $this->old_filename = '';
        
        if ( ! $this->CI->upload->do_upload($column))
        {
            $this->upload_error_message = $this->CI->upload->display_errors();
            return FALSE;
        }
        else
        {
            //ファイル名を生成
            $this->_filename();

            $image_data = $this->CI->upload->data($column);
            
            //元のサイズを保存する場合(縮小なしで保存)
            if ($rename)
            {
                $option[] = '';
            }

            foreach ($option as $val)
            {
                if ( ! $this->_image_risize($image_data, $val))
                {
                    return FALSE;
                }
                $this->upload_filename[$column]['new_filename'] = $this->new_filename;
                $this->upload_filename[$column]['old_filename'] = $this->old_filename;
            }

            if ($deleted)
            {
                unlink($image_data['full_path']);
            }
            
            return TRUE;
        }
    }

    private function _filename()
    {
        $u_id = $this->CI->session->userdata('user_id');

        $filename_prefix = date('Y').date('m').$u_id;
        $file_token      = md5(uniqid($filename_prefix));

        $this->file_token = $file_token;
    }

    private function _image_risize($img_data, $size = '')
    {
        //サイズが指定されていない場合は、ファイルそのものをコピーする
        if ( ! $size)
        {
            if ( ! copy($img_data['full_path'], $img_data['file_path'].$this->file_token.$img_data['file_ext']))
            {
                $this->upload_error_message = 'ファイルのコピーに失敗しました。';
                return FALSE;
            }
            else
            {
                return TRUE;
            }
        }
        else
        {
            //画像操作
            $img_conf['image_library']  = 'gd2';
            $img_conf['source_image']   = $img_data['full_path'];
            $img_conf['create_thumb']   = FALSE;
            $img_conf['maintain_ratio'] = TRUE;
            $img_conf['new_image']      = $img_data['file_path'].$this->file_token.$img_data['file_ext'];

            $s_width  = $img_data['image_width'];
            $s_height = $img_data['image_height'];
            $w = $size;
            $h = $size;
            if (!$w) $w = $s_width;
            if (!$h) $h = $s_height;
            $image_resize = $this->_create_image_size($s_width, $s_height, $w, $h);

            //指定サイズで処理
            $img_conf['width']  = $image_resize['width'];
            $img_conf['height'] = $image_resize['height'];
            $img_conf['new_image'] = $img_data['file_path'].$size.'_'.$this->file_token.$img_data['file_ext'];
            $this->initialize($img_conf);
            if ($this->resize())
            {
                $this->new_filename = $this->file_token.$img_data['file_ext'];
                $this->old_filename = $img_data['file_name'];
                return TRUE;
            }
            else
            {
                $this->clear();
                $this->upload_error_message = '画像リサイズ時にエラーが発生しました。';
                return FALSE;
            }
        }
    }

    private function _create_image_size($s_w, $s_h, $w, $h)
    {
        // リサイズの必要がない場合
        if ($s_w <= $w && $s_h <= $h)
        {
            //そのまま
            $img_resize['width']  = $s_w;
            $img_resize['height'] = $s_h;
        }
        else
        {
            // 出力サイズ変更
            $o_width  = $s_w;
            $o_height = $s_h;

            if ($w < $s_w)
            {
                $o_width  = $w;
                $o_height = $s_h * $w / $s_w;
                if ( $o_height < 1 )
                {
                    $o_height = 1;
                }
            }
            if ($h < $o_height && $h < $s_h)
            {
                $o_width  = $s_w * $h / $s_h;
                $o_height = $h;
                if ( $o_width < 1 )
                {
                    $o_width = 1;
                }
            }

            $img_resize['width']  = $o_width;
            $img_resize['height'] = $o_height;
        }

        return $img_resize;
    }
}
// END MYNETS_Image_lib Class

/* End of file MYNETS_Image_lib.php */
/* Location: ./system/mynets/libraries/MYNETS_Image_lib.php */

コントローラでの使い方はこちら。

    function test_2()
    {
        $this->load->library('image_lib');
        $config['upload_path'] = 'test/';
        $this->image_lib->execute($config);
        $option = array('180','120','60');
        
        $error_msg = array();
        
        if ($this->image_lib->is_upload('image_filename_1'))
        {
            if ( ! $this->image_lib->convert_image('image_filename_1', $option, TRUE))
            {
                $error_msg['image_filename_1'] = $this->image_lib->upload_error_message;
            }
        }
        if ($this->image_lib->is_upload('image_filename_2'))
        {
            if ( ! $this->image_lib->convert_image('image_filename_2', $option, TRUE))
            {
                $error_msg['image_filename_2'] = $this->image_lib->upload_error_message;
            }
        }
        if ($this->image_lib->is_upload('image_filename_3'))
        {
            if ( ! $this->image_lib->convert_image('image_filename_3', $option, TRUE))
            {
                $error_msg['image_filename_3'] = $this->image_lib->upload_error_message;
            }
        }
        if ($this->image_lib->is_upload('image_filename_4'))
        {
            if ( ! $this->image_lib->convert_image('image_filename_4', $option, TRUE))
            {
                $error_msg['image_filename_4'] = $this->image_lib->upload_error_message;
            }
        }
        //print_r($this->image_lib->upload_filename);
    }

簡単に解説すると、コントローラでは、image_libライブラリをロードします。
次に、そこで取り扱うファイルの保存ディレクトリを指定します。
※基本的に、CIのROOTの下にあるuploads/ディレクトリというのを画像保存の既定場所としています。
private $upload_base = 'uploads/';
private $max_size = '500'; //500KByteまで
private $max_width = '1024'; //1024px
private $max_height = '1024'; //1024px
private $overwrite = TRUE; //上書きするか
private $allowed_types = 'gif|jpg|png';

コントローラで指定するのは、そのディレクトリ以下に保存する場所を指す、ということになります。
※このあたりはアプリによって変更したり、仕組みを変えることもありでしょうね。
現在未指定の場合はエラーを返すようにしています。
ここで、たとえば、日記の画像を扱うコントローラの場合、
$config['upload_path'] = 'diary/';
や、さらに会員IDをつけたディレクトリを設定することでそのディレクトリでの保存となります。
※DBでのオブジェクトの保存は現在作成していませんが、今後作成する、かもしれません。
それを、
$this->image_lib->execute($config);
executeメソッドで引き渡し、次に
$option = array('180','120','60');
$this->image_lib->convert_image('image_filename_1', $option, TRUE)
としています。
$optionに渡すのは、縮小するための画像サイズを指定します。
仮に$optionに何も渡さない場合、特に何もしません。
※サイズ変更なしでその画像のみを扱う場合は、リネームのみしたほうがいいかもしれません。またはアップロード処理だけを行うとか。
あくまでも現在は投稿された画像をリネームし、サイズを調整して格納するという時に使います。
convert_imageメソッドの引数は、
投稿されるときに使う<input type="file" name="image_filename_1">のフィールド名を渡します。
これにより、そのフィールドに画像がアップされた場合、処理をすることになります。
二つ目は縮小するときのサイズを配列で。
3つめは元画像を削除するかどうか、
そして4つ目の引数として、オリジナルサイズの画像も、ハッシュ化した画像ファイル名に置き換えるかどうか。

これを使うこととで、
複数ファイルをアップした場合、
指定したディレクトリに、指定したサイズの画像をリサイズして複数同時に作成します。

処理を行った場合、プロパティで元の画像ファイル名、内部で生成したハッシュ化した画像ファイル名トークン部分が取り出せます。
リサイズしたファイルは、それぞれ120_XXXXX等として格納されています。

これで少しコントローラの記述がらくになるかな。