2011年12月8日

Yii Framework: 画像アップロードアクションを作る

( 2013-10-07 に更新しました )

Yii (v1.1.14) で画像アップロードのコードを書いてみます。サンプル用のテーブルとして以下を作ってみました。

CREATE TABLE `item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL COMMENT 'タイトル',
  `file_name` varchar(255) NOT NULL COMMENT 'ファイル名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

画像は images/item/ に保存し、file_name カラムにはファイル名を保存する形をとります。とりあえず最初に Gii の Model Generator で Item モデルを、Controller Generator で item のコントローラ、ビューを作成しておきます。

モデル:
<?php
class Item extends CActiveRecord
{
const ALLOW_TYPES = 'jpg, png, gif';
public $image;
...
/**
* @see CModel::attributeLabels()
*/
public function attributeLabels()
{
return array(
'title' => 'タイトル',
'image' => '画像',
);
}
/**
* @see CModel::rules()
*/
public function rules()
{
$imageRule = array(
'image',
'file',
'safe' => true,
'types' => self::ALLOW_TYPES,
'mimeTypes' => 'image/jpeg, image/pjpeg, image/png, image/x-png, image/gif',
'maxSize' => 1024 * 1024 * 1,
'message' => '{attribute} が選択されていません。',
'wrongType' => '{attribute} の拡張子が正しくありません (' . self::ALLOW_TYPES . ' のみ)',
'wrongMimeType' => '{attribute} のファイル形式が正しくありません。',
'tooLarge' => '{attribute} のファイルサイズが上限を超えています。(1MB まで)',
);
return array(
// title
array('title', 'required'),
array('title', 'length', 'max'=>100),
// file_name
array('file_name', 'safe'),
// image
array_merge($imageRule, array('on'=>'insert')),
array_merge($imageRule, array('allowEmpty'=>true, 'on'=>'update')),
);
}
}
view raw Item.php hosted with ❤ by GitHub
const で "jpg, png, gif" という文字列を定義しておきます (数カ所で使うのでここで定義しておきました) 。 モデルで public なプロパティ $image の追加も。 rules() ではファイルアップロードに関するバリデーションがちょっと複雑ですが、これは CFileValidator に詳しく載っています。 拡張子が jpg, png, gif のみを許可し、MIME タイプもそれらに関連するもののみ許可、ファイルサイズは 1MB 。 更新時には画像を変更してもしなくてもいいように 'allowEmpty'=>true を update シナリオでは付加しています。

ちなみに mimeTypes は finfo 系の関数がなけれが 非推奨の mime_content_type を使ったりするので、セキュリティのことを考えると PHP 5.3.0 以降を使うのが無難だと思います。

コントローラ:
<?php
class ItemController extends Controller
{
public $uploadedFileUrl;
private $uploadedFilePath;
/**
* @see CController::init()
*/
public function init()
{
parent::init();
$this->uploadedFileUrl =
Yii::app()->baseUrl .
'/images/item/';
$this->uploadedFilePath =
Yii::getPathOfAlias('webroot.images.item') .
DIRECTORY_SEPARATOR;
}
...
/**
* Creates a new item.
*/
public function actionCreate()
{
$item = new Item();
if (isset($_POST['Item'])) {
$item->attributes = $_POST['Item'];
if ($item->validate()) {
$image = CUploadedFile::getInstance($item, 'image');
$item->file_name = sha1(mt_rand() . microtime()) . '.' . $image->extensionName;
$image->saveAs($this->uploadedFilePath . $item->file_name);
$item->save(false);
$this->redirect(array('index'));
}
}
$this->render('_form', compact('item'));
}
...
init() でアップロードしたファイルの URL とパスを初期化しています。 CUploadedFile::getInstance() でアップロードされたファイルの情報を取得して、 saveAs() で任意の場所に保存する、という流れです ( 詳しくは CUploadedFile ) 。

ビュー:
<div class="form">
<?php echo CHtml::form('', 'post', array('enctype' => 'multipart/form-data')); ?>
<div class="row">
<?php echo CHtml::activeLabel($item, 'title'); ?>
<?php echo CHtml::activeTextField($item, 'title'); ?>
<?php echo CHtml::error($item, 'title'); ?>
</div><!-- /.row -->
<div class="row">
<?php echo CHtml::activeLabel($item, 'image'); ?>
<div class="hint">ファイル形式は <?php echo CHtml::encode(Item::ALLOW_TYPES); ?> のみで、ファイルサイズは 1MB まで</div>
<?php echo CHtml::activeFileField($item, 'image'); ?>
<?php echo CHtml::error($item, 'image'); ?>
</div><!-- /.row -->
<div class="row">
<?php echo CHtml::submitButton($item->isNewRecord ? 'Create' : 'Update'); ?>
</div><!-- /.row -->
<?php echo CHtml::endForm(); ?>
</div><!-- /.form -->
view raw _form.php hosted with ❤ by GitHub
ビューは CHtml::form() で enctype ... を指定するくらいです。

参考リンク

0 件のコメント:

コメントを投稿