Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
57
Help us understand the problem. What is going on with this article?
@NULLchar

PHPとMySQLを利用した画像・動画のアップロード・保存・表示

More than 3 years have passed since last update.

やること

  • 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());
    }
?>

参考文献

57
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
NULLchar
未来の情報通信屋だと思います

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
57
Help us understand the problem. What is going on with this article?