HOME > WEBプログラム覚書 > [PHP]MySQLを利用した画像の保存と出力

[PHP]MySQLを利用した画像の保存と出力

PHPで画像を取得してMySQLに保存、そしてMySQLからデータを取得して表示させる方法を そろそろ書かないとまずい気がしたので書いておきます。 画像をデータベースに保存するのには、とても重要な意味があります。

その前に、まずは仕組みから。

仕組み

特に難しいことはないのですが、意外と知らない人も多いのではないでしょうか? 僕もPHPやる前は知りませんでしたw

上図が画像ファイルにアクセスして表示されるまでの簡単な流れです。 まぁこの流れ自体は別にどうでもよくて、今回もっとも重要になるのがレスポンスヘッダのcontent-typeです。 content-typeに絡むWEBサーバとブラウザの役割は、

WEBサーバ

拡張子に応じて適切なcontent-typeをheaderに含めてレスポンスする。

ブラウザ

レスポンスに含まれるcontent-typeに応じたレンダリングを行う。

になります。つまり、

ブラウザにとっては「.jpgだから画像なのではなく、Content-typeがimage/jpegだから画像」なのです。 逆に言うと「Content-typeがimage/jpegなら.phpでも.htmlでもブラウザは画像として処理する」のです。

同じファイルをContent-typeを変えて表示した場合のサンプル

Content-type: image/jpeg
PHP
  1.  <?php
  2.  $img = file_get_contents('./img.jpg');
  3.  header('Content-type: image/jpeg');
  4.  echo $img;
  5.  ?>
Content-type: text/html
PHP
  1.  <?php
  2.  $img = file_get_contents('./img.jpg');
  3.  header('Content-type: text/html');
  4.  echo $img;
  5.  ?>

画像の種類に応じたcontent-typeをレスポンスしてあげればよいということです。 あとはブラウザががんばって処理してくれます。

また通常はWEBサーバ(apacheなど)が、拡張子に応じて適切なContent-typeを返してくれるので content-typeについてあれこれ考える必要はありません。

じゃあなぜこんなことを説明してるのか?

データベースに保存した様々な種類の画像(jpg、gif、pngなど)を扱う場合など 一つのファイルで様々な種類の役割ができると、とても便利です。

クライアント(htmlのimgなど)は、どのkeyを指定したらどんな画像が出てくるのかさえわかっていればよく 画像の拡張子を気にしなくて済むからです。

ですがその場合、WEBサーバは適切なContent-typeを出力できないので、 自分で指定してあげる必要があるのです。上記のことがわかってないと 「画像が表示されない!」「文字化け?」というように悩むことになるので予め説明しました。

MySQLデータベースの作成

適当にテーブルを作成。

SQL
  1.  CREATE DATABASE secret;
  2.  
  3.  CREATE TABLE images (
  4.  id int NOT NULL AUTO_INCREMENT,
  5.  ext varchar(5),
  6.  contents blob,
  7.  PRIMARY KEY (id)
  8.  );

バイナリを扱えるblob型はサイズにより4種類あるので状況に合わせて選択します。

画像の取得とデータベースへの保存

insert.php
  1.  <?php
  2.  // 画像と拡張子を取得
  3.  $img_path = '画像のパス';
  4.  $img = file_get_contents($img_path);
  5.  $ext = pathinfo($img_path, PATHINFO_EXTENSION);
  6.  
  7.  // データベースに保存
  8.  $pdo = new PDO('mysql:host=HOST; dbname=DBNAME', 'USER', 'PASSWORD');
  9.  $stmt = $pdo->prepare('INSERT INTO images VALUES(0, :ext, :img)');
  10.  $stmt->bindParam(':ext', $ext);
  11.  $stmt->bindParam(':img', $img);
  12.  $stmt->execute();
  13.  ?>

画像の出力

img.php
  1.  <?php
  2.  // クエリの取得
  3.  if (preg_match('/^[0-9]+$/', $_GET['key'])) {
  4.   $id = $_GET['key'];
  5.  } else {
  6.   throw new Exception('エラー');
  7.  }
  8.  
  9.  // データベースから対象のデータを取得
  10.  $pdo = new PDO('mysql:host=HOST; dbname=DBNAME', 'USER', 'PASSWORD');
  11.  $stmt = $pdo->prepare('SELECT ext, contents FROM images WHERE id=:id');
  12.  $stmt->bindParam(':id', $id);
  13.  $stmt->execute();
  14.  
  15.  // Content-typeテーブル
  16.  $contents_type = array(
  17.   'jpg' => 'image/jpeg',
  18.   'jpeg' => 'image/jpeg',
  19.   'png' => 'image/png',
  20.   'gif' => 'image/gif',
  21.   'bmp' => 'image/bmp',
  22.  );
  23.  
  24.  // 出力
  25.  $img = $stmt->fetchObject();
  26.  header('Content-type: ' . $contents_type[$img->ext]);
  27.  echo $img->contents;
  28.  ?>

img.php?key=1で直接アクセスしてもいいし、hrefの値としてもOK。

なぜ画像をデータベースに保存するのか?

それはもちろんセキュリティのためです。それ以外理由はありません。 データベースに画像を保存しておくことで、大部分の攻撃を回避することが可能となります。

そして画像がいけるということは動画もいけます。 ただ、サイズが大きいと設定ファイルを変更しないとダメかもです。

PHPからINSERTしてSQLSTATE は00000が返ってきてるのに保存されてなかったり、 mysqlで直接やってもエラーは出ないのに保存されなかったり0バイトになる場合、 my.ini変更するとうまくいきます。

PHP
  1.  <?php
  2.  INSERT INTO images VALUES(0, 'wmv', load_file('~/test.wmv'));
  3.  ?>

my.iniのmax_allowed_packetを変更してみましょう。

my.ini
  1.  #max_allowed_packet = 1M
  2.  max_allowed_packet = 16M

どの値が適切かはいまいちよく分かりませんが・・・

2012/03/04 修正

insert.php

AUTO_INCREMENTの設定されているカラムへのINSERTする値を0に変更

  • 環境により""でもINSERTができる場合がありますが、0またはNULLが正しいようです。または関数。

    No value was specified for the AUTO_INCREMENT column, so MySQL assigned sequence numbers automatically. You can also explicitly assign NULL or 0 to the column to generate sequence numbers. MySQL :: MySQL 5.0 Reference Manual :: 3.6.9 Using AUTO_INCREMENT

引数として"画像のパス"と記述していた部分を$img_pathに変更

  • 一番最初に$img_pathを定義してる意味がなかったので。

PHP逆引きレシピ 第2版 (PROGRAMMER’S RECiPE)

作者:安藤 建一 | 価格:¥ 3,024

投稿日 2010年4月 2日 04:22
カテゴリ MySQL | PHP
タグ サンプルコード | データベース | ブラウザ
トラックバック URL http://www.kantenna.com/cgi-bin/mt504/mt-tb.cgi/1210

コメント

ばぐだらけじゃん

はじめまして、MySQL勉強中の者です。
画像の保存と出力の参考にさせて頂きました。
ありがとうございます。

PHPにて、mysqliの手続き型の方で似たようなコードを書いた際に、どうしても保存の際のクエリ実行の時にシンタックスエラーが出ました。
色々と悩みながら解決させたのですが、$img(画像のバイナリデータ)をmysqli_real_escape_stringでエスケープ処理をする事でエラーを回避する事が出来ました。

PDOクラスは使用した事がないので、エスケープ処理なしでもOKなのかは分かりませんが、一言あると大変助かりました。

説明もサンプルコードも非常に丁寧に書いて頂いてるので、非常にありがたかったです。

わかりやすいサンプルソースがご提示下さいましてどうもありがとうございます。

画像が壊れていて表示されずに困っております。

ブラウザは
Google Chrome 32.0.1700.107m

Firefox 26.0

データベースの文字コードが正しく設定されていないために表示されないのでしょうか?

ご存知でいらっしゃましたら教えてください。

また、他に留意する点があればお願いします。


コメントする
Name
Email Address
URL