やること
- PHPでアップロードされた画像・動画(以下メディア)をDBに格納する.メディアはバイナリデータにして格納します.
- 格納されたDBからメディアデータを取り出し,画面上に表示する.
実行環境
- 開発言語: PHP 5.6.30
- ブラウザ: Google Chrome 61.0.3163.100
- サーバー: Apache/2.4.25 (Unix)
- DB: MySQL 5.7.18
それぞれのバージョン情報は多少違っていても(PHPはver.7でも)動くと思います.
追記:2018/02/07 15:54 PHP ver7.1での動作を確認しました.
実装
テーブル構造
インデックスに加えて,メディアのファイルの名前,拡張子,バイナリデータそのものをDBに格納します.テーブルの名前は「media」,DBの名前は適当に「mediatest」とでもしてやります.
CREATE TABLE
`mediatest`.`media` ( `id` INT NOT NULL AUTO_INCREMENT ,
`fname` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`extension` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`raw_data` LONGBLOB NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;
ソースコード
必要なプログラムは,「アップロードされたメディアをDBに格納する」プログラムと「格納されたDBからメディアデータを取り出し,画面上に表示する」プログラムの2つです.
前者はindex.php,後者はimport_media.phpで実装しています.
また,DBへの接続にはPDOを使用します.
index.php
<?php
try{
$user = "root";
$pass = "";
$pdo = new PDO("mysql:host=127.0.0.1;dbname=mediatest;charset=utf8", $user, $pass);
//ファイルアップロードがあったとき
if (isset($_FILES['upfile']['error']) && is_int($_FILES['upfile']['error']) && $_FILES["upfile"]["name"] !== ""){
//エラーチェック
switch ($_FILES['upfile']['error']) {
case UPLOAD_ERR_OK: // OK
break;
case UPLOAD_ERR_NO_FILE: // 未選択
throw new RuntimeException('ファイルが選択されていません', 400);
case UPLOAD_ERR_INI_SIZE: // php.ini定義の最大サイズ超過
throw new RuntimeException('ファイルサイズが大きすぎます', 400);
default:
throw new RuntimeException('その他のエラーが発生しました', 500);
}
//画像・動画をバイナリデータにする.
$raw_data = file_get_contents($_FILES['upfile']['tmp_name']);
//拡張子を見る
$tmp = pathinfo($_FILES["upfile"]["name"]);
$extension = $tmp["extension"];
if($extension === "jpg" || $extension === "jpeg" || $extension === "JPG" || $extension === "JPEG"){
$extension = "jpeg";
}
elseif($extension === "png" || $extension === "PNG"){
$extension = "png";
}
elseif($extension === "gif" || $extension === "GIF"){
$extension = "gif";
}
elseif($extension === "mp4" || $extension === "MP4"){
$extension = "mp4";
}
else{
echo "非対応ファイルです.<br/>";
echo ("<a href=\"index.php\">戻る</a><br/>");
exit(1);
}
//DBに格納するファイルネーム設定
//サーバー側の一時的なファイルネームと取得時刻を結合した文字列にsha256をかける.
$date = getdate();
$fname = $_FILES["upfile"]["tmp_name"].$date["year"].$date["mon"].$date["mday"].$date["hours"].$date["minutes"].$date["seconds"];
$fname = hash("sha256", $fname);
//画像・動画をDBに格納.
$sql = "INSERT INTO media(fname, extension, raw_data) VALUES (:fname, :extension, :raw_data);";
$stmt = $pdo->prepare($sql);
$stmt -> bindValue(":fname",$fname, PDO::PARAM_STR);
$stmt -> bindValue(":extension",$extension, PDO::PARAM_STR);
$stmt -> bindValue(":raw_data",$raw_data, PDO::PARAM_STR);
$stmt -> execute();
}
}
catch(PDOException $e){
echo("<p>500 Inertnal Server Error</p>");
exit($e->getMessage());
}
?>
<!DOCTYPE HTML>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>media</title>
</head>
<body>
<form action="index.php" enctype="multipart/form-data" method="post">
<label>画像/動画アップロード</label>
<input type="file" name="upfile">
<br>
※画像はjpeg方式,png方式,gif方式に対応しています.動画はmp4方式のみ対応しています.<br>
<input type="submit" value="アップロード">
</form>
<?php
//DBから取得して表示する.
$sql = "SELECT * FROM media ORDER BY id;";
$stmt = $pdo->prepare($sql);
$stmt -> execute();
while ($row = $stmt -> fetch(PDO::FETCH_ASSOC)){
echo ($row["id"]."<br/>");
//動画と画像で場合分け
$target = $row["fname"];
if($row["extension"] == "mp4"){
echo ("<video src=\"import_media.php?target=$target\" width=\"426\" height=\"240\" controls></video>");
}
elseif($row["extension"] == "jpeg" || $row["extension"] == "png" || $row["extension"] == "gif"){
echo ("<img src='import_media.php?target=$target'>");
}
echo ("<br/><br/>");
}
?>
</body>
</html>
import_media.php
<?php
if(isset($_GET["target"]) && $_GET["target"] !== ""){
$target = $_GET["target"];
}
else{
header("Location: index.php");
}
$MIMETypes = array(
'png' => 'image/png',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'mp4' => 'video/mp4'
);
try {
$user = "root";
$pass = "";
$pdo = new PDO("mysql:host=127.0.0.1;dbname=mediatest;charset=utf8", $user, $pass);
$sql = "SELECT * FROM media WHERE fname = :target;";
$stmt = $pdo->prepare($sql);
$stmt -> bindValue(":target", $target, PDO::PARAM_STR);
$stmt -> execute();
$row = $stmt -> fetch(PDO::FETCH_ASSOC);
header("Content-Type: ".$MIMETypes[$row["extension"]]);
echo ($row["raw_data"]);
}
catch (PDOException $e) {
echo("<p>500 Inertnal Server Error</p>");
exit($e->getMessage());
}
?>