[ステップアップ! CakePHP]画像をデータベースに保存する
iPad、すでに3,000円以上のアプリを買ってるtanakaです。GoodReader初めて使いましたが便利ですね!
CakePHP連載6回目。今回はアップロードされた画像をデータベースに保存する方法を紹介します。
実はあんまりCakePHPとは関係ないですが、CakePHPのおかげで実装が容易になる部分もありますので紹介します。
画像格納用テーブル
まずは画像を保存するためのテーブルを用意します。
CREATE TABLE `images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`filename` varchar(60) NOT NULL,
`contents` mediumblob NOT NULL,
PRIMARY KEY (`id`)
);
filenameは、URL中のファイル名として使います。(URLにidを使う場合は不要)contentsが、画像ファイルの中身を入れるカラムです。
大きめなデータを保存するにはtext型かblob型を使います。ここではblob型のひとつmediumblob型を選びます。mediumblob型を選ぶのは、単なるblob型だと約60キロバイトまでしか保存できず、mediumblob型は約16メガバイトまで保存できるためです。16メガバイトなら、たいていの場合、画像を保存するには十分です。
アップロード用フォーム views/images/index.ctp
まずは、ImagesControllerから解説といきたいところですが、単にフォームを表示するだけでいいので、テンプレートをまず紹介します。
<?php e($form->create(null, array('type'=>'file', 'action'=>'add')));?>
<?php e($session->flash());?>
<?php e($form->file('image'));?>
<?php e($form->submit('画像を追加'));?>
<?php e($form->end());?>
<h2>追加した画像</h2>
<ul>
<?php foreach ($images as $image) { ?>
<li><?php e($html->link("/images/contents/{$image['Image']['filename']}"));?></li>
<?php } ?>
</ul>
上にアップロード用フォームがあります。ファイルアップロードできるよう'type'=>'file'と記述する以外は普通のフォームです。
下には追加された画像にアクセス出来るよう一覧を表示します。
アップロード〜保存処理 ImagesController::add() (images_controller.php)
フォームからのアップロードはaddアクションで処理します。
<?php
class ImagesController extends AppController {
// ... 中略
/**
* 画像を登録する
*/
function add(){
$limit = 1024 * 1024;
// 画像の容量チェック
if ($this->data['Image']['image']['size'] > $limit){
$this->Session->setFlash('1MB以内の画像が登録可能です。');
$this->redirect('index');
}
// アップロードされた画像か
if (!is_uploaded_file($this->data['Image']['image']['tmp_name'])){
$this->Session->setFlash('アップロードされた画像ではありません。');
$this->redirect('index');
}
// 保存
$image = array(
'Image' => array(
'filename' => md5(microtime()) . '.jpg',
'contents' => file_get_contents($this->data['Image']['image']['tmp_name']),
)
);
$this->Image->save($image);
$this->Session->setFlash('画像をアップロードしました。');
$this->redirect('index');
}
// ... 中略
}
まずは、入力を検査します。あまり大きなファイルをアップロードしてもらいたくないので、1メガバイトに制限します。また、本当にアップロードされたファイルなのかもチェックします。
こういったチェックはモデルのカスタムバリデーションメソッドで記述すれば、コントローラのロジックがすっきりするのでオススメです。
検査が終わったら、データベースに保存しましょう。ファイル名は一意になるようにして、画像の中身はfile_get_contentsでバイナリ列を取得して保存します。
データベース内の画像データを表示する
最後に、データベースに保存した画像を表示します。
function contents($filename) {
$this->layout = false;
$image = $this->Image->findByFilename($filename);
if (empty($image)) {
$this->cakeError('error404');
}
header('Content-type: image/jpeg');
echo $image['Image']['contents'];
}
ファイル名形式の文字列を$filenameで受け取り、データを取り出します。次に余計なものが表示されないよう、$this->layoutを無効にします。
ブラウザにJPEG画像として扱ってもらうようにヘッダを送出し、画像をレンダリングします。
コントローラのコード全体
フォームのテンプレートは上記の通りですので、最後にコントローラのコードを載せます。
<?php
class ImagesController extends AppController {
var $uses = array('Image');
function index(){
$images = $this->Image->find('all');
$this->set(compact('images'));
}
/**
* 画像を登録する
*/
function add(){
$limit = 1024 * 1024;
debug($this->data);
// 画像の容量チェック
if ($this->data['Image']['image']['size'] > $limit){
$this->Session->setFlash('1MB以内の画像が登録可能です。');
$this->redirect('index');
}
// アップロードされた画像か
if (!is_uploaded_file($this->data['Image']['image']['tmp_name'])){
$this->Session->setFlash('アップロードされた画像ではありません。');
$this->redirect('index');
}
// 保存
$image = array(
'Image' => array(
'filename' => md5(microtime()) . '.jpg',
'contents' => file_get_contents($this->data['Image']['image']['tmp_name']),
)
);
$this->Image->save($image);
$this->Session->setFlash('画像をアップロードしました。');
$this->redirect('index');
}
function contents($filename) {
$this->layout = false;
$image = $this->Image->findByFilename($filename);
if (empty($image)) {
$this->cakeError('error404');
}
header('Content-type: image/jpeg');
echo $image['Image']['contents'];
}
}
まとめ
画像のアップロードからデータベースへの保存、画像表示までの流れを紹介しました。