PHP

PHPでの画像の保存・表示方法まとめ

今回は画像を「フォルダに保存して表示」、「そのまま表示」、「DBに保存して表示」
の3パターンをまとめました。

画像をフォルダに保存して表示

formからファイルupして保存する

upload.html(画像アップロードフォーム)
    <form method="POST" action="upimg.php" enctype="multipart/form-data">

        <input type="file" name="upimg" accept="image/*">
        <input type="submit">

    </form>
upimg.php
    $img_name = $_FILES['upimg']['name'];

    //画像を保存
    move_uploaded_file($_FILES['upimg']['tmp_name'], './upload/' . $img_name);

    echo '<img src="img.php?img_name=' . $img_name . '">';

外部サイトから取得して保存する

    $url = "https://www.google.co.jp/images/srpr/logo11w.png";
    $img = file_get_contents($url);

    $imginfo = pathinfo($url);

    $img_name = $imginfo['basename'];

    //画像を保存
    file_put_contents('./upload/' . $img_name, $img);

    echo '<img src="img.php?img_name=' . $img_name . '" />';

保存した画像を表示

img.php(画像表示用PHP)
    $img_name = $_GET['img_name'];

    $img_dir = './upload/' . $img_name;

    $imginfo = getimagesize($img_dir);

    header('Content-Type: ' . $imginfo['mime']);
    readfile($img_dir);

今回はformでupされたファイル名をそのまま画像名として保存していますが、
脆弱性の危険性があるので名前を変える必要があります。
ポイントは「img.php」にファイル名を渡してreadfile関数を使い、ディレクトリ内の画像を表示しています。
ちなみにpathinfoで取得した「extension」は画像名から取得した拡張子なのでmimeTypeとしては信用できないです。

画像をそのまま表示

formからファイルupしてそのまま表示

upimg.php
    $fp = fopen($_FILES['upimg']['tmp_name'], "rb");
    $img = fread($fp, filesize($_FILES['upimg']['tmp_name']));
    fclose($fp);

    $enc_img = base64_encode($img);

    $imginfo = getimagesize('data:application/octet-stream;base64,' . $enc_img);

    echo '<img src="data:' . $imginfo['mime'] . ';base64,'.$enc_img.'">';

外部サイトから取得してそのまま表示


    $url = "https://www.google.co.jp/images/srpr/logo11w.png";
    $img = file_get_contents($url);

    $enc_img = base64_encode($img);

    $imginfo = getimagesize('data:application/octet-stream;base64,' . $enc_img);

    echo '<img src="data:' . $imginfo['mime'] . ';base64,'.$enc_img.'">';

ディレクトリ内に保存することもなくそのままの表示するので比較的安全。

DBに保存して表示

DBの中身はシンプルにIDとBLOBを保存するカラムだけです。

sql
    CREATE TABLE IF NOT EXISTS PICTURE (
        PICID   INT             PRIMARY KEY,
        PIC     MEDIUMBLOB      DEFAULT NULL
    );

formからファイルupしてDBに保存

upimg.php
    $pic_id = 1;

    $fp = fopen($_FILES['upimg']['tmp_name'], "rb");
    $img = fread($fp, filesize($_FILES['upimg']['tmp_name']));
    fclose($fp);

    $sql = <<<SQL
        REPLACE INTO PICTURE
        (PICID, PIC) VALUES (:pic_id, :PIC);
SQL;
    $stmt = $DB->prepare($sql);
    $stmt->bindValue(':pic_id'  ,$pic_id);
    $stmt->bindValue(':PIC'     ,$img);
    $stmt->execute();
    $stmt = null;

    echo '<img src="img.php?pic_id=' . $pic_id . '">';

外部サイトから取得してDBに保存

get_img.php
    $pic_id = 2;

    $url = "https://www.google.co.jp/images/srpr/logo11w.png";
    $img = file_get_contents($url);

    $sql = <<<SQL
        REPLACE INTO PICTURE
        (PICID, PIC) VALUES (:pic_id, :PIC);
SQL;
    $stmt = $DB->prepare($sql);
    $stmt->bindValue(':pic_id'  ,$pic_id);
    $stmt->bindValue(':PIC'     ,$img);
    $stmt->execute();
    $stmt = null;

    echo '<img src="img.php?pic_id=' . $pic_id . '">';

DBに保存した画像を表示

img.php(画像表示用)
    $pic_id = $_GET['pic_id'];

    //画像取得
    $sql = <<<SQL
        SELECT PIC FROM PICTURE WHERE PICID = :pic_id
SQL;
    $stmt = $DB->prepare($sql);
    $stmt->bindValue(':pic_id'  ,$pic_id);
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    $DB_PIC = $row['PIC'];

    $finfo    = finfo_open(FILEINFO_MIME_TYPE);
    $mimeType = finfo_buffer($finfo, $DB_PIC);
    finfo_close($finfo);

    header('Content-Type: ' . $mimeType);
    echo $DB_PIC;

一度IDを決めてDBに保存。「img.php」に先ほど保存した画像のIDを渡して表示させます。
DBにBLOBのデータを保存する際に「ON DUPLICATE KEY UPDATE」を使って
上書き保存させようとしたのですが、出来なかったので
「REPLACE INTO」を使って差し替えています。

DBに保存してある画像をまとめて表示

    $sql = <<<SQL
        SELECT PIC FROM PICTURE
SQL;
    $stmt = $DB->prepare($sql);
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    $stmt->execute();
    while($row = $stmt->fetch(PDO::FETCH_ASSOC) ){
        $DB_PIC_ARRAY[] = $row['PIC'];
    }
    $stmt = null;

    foreach($DB_PIC_ARRAY as $pic){
        $enc_img = base64_encode($pic);
        $imginfo = getimagesize('data:application/octet-stream;base64,' . $enc_img);
        echo '<img src="data:' . $imginfo['mime'] . ';base64,' . $enc_img . '" />';
    }

まとめ

画像を扱う場合は脆弱性の危険性「../ をファイル名に含めて攻撃 (ディレクトリトラバーサル)」といったものがあるので注意が必要です。
基本的には画像をディレクトリ内に保存するのはやめた方が良いです。(別サーバーに保存するなどの対策)

参考リンク
PHPでファイルのMIMEタイプ取得に何を使う?explode? pathinfo? finfo_file?
【PHP】画像ファイルの種類(拡張子やMIMEタイプ)を取得する方法 – ysklog