非公開ディレクトリに画像を保存・読込
実装概要
今回の実装では、非公開ディレクトリへの画像の保存と取り出しを行います。特に、ログイン認証が必要なサイトでも、画像を公開ディレクトリに保存した場合は、URLを知っていれば、ログインしなくても画像を観覧することができたり、ツール等で、まるごとダウンロードされてしまう等の問題があります。今回は、それに対応するために、画像を非公開ディレクトリに保存して、そこから取り出す部分を紹介します。保存については、一般的な保存と変わりませんが、画像を表示する際には、非公開ディレクトリに保存してあるので、直リンクでのアクセスは不可能です。そこで、PHP側で非公開ディレクトリから画像を取ってくるという作業が必要となります。
サンプルの画像のURIをみても分かるように、直接画像を参照するのではなく、img.php?img=XXXXXXX.jpgのGETパラメーターとして、画像を指定していることに注目してください。
保存先の設定とフォームの作成
まず保存先のパスを定数として定義しておきます。場所は、ドキュメントルートよりも上の/public_htmlと同じ階層にimgフォルダを作っておきます。このあたりは、サーバー環境によっても若干ことなると思いますので、定数に定義するパスは自身の環境にあわせて変更ください。
【define.php】
<?php define('IMAGES_DIR', '/home/xxxxxx/img/'); ?>
次に、画像アップロードフォームの作成です。
【index.html】
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Sample</title> </head> <body> <form action="upload.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="1048576"> <input type="file" name="img" accept="image/*" /> <input type="submit" name="send" value="送信"> </form> </body> </html>
非公開ディレクトリへ画像をアップロードする
アップロードに関しては、このあたりは、前半は、バリテーションで、後半が問題がなければ、アップロードです。
<?php require 'define.php'; error_reporting(E_ALL & ~E_NOTICE); // E_NOTICE 以外の全てのエラーを表示する //バリテーション if ($_FILES['img']['error'] != UPLOAD_ERR_OK) { echo "エラーが発生しました : ".$_FILES['img']['error']. "<br>"; } $size = filesize($_FILES['img']['tmp_name']); if (!$size || $size > $_POST['MAX_FILE_SIZE']) { echo $_FILES['img']['name']." のファイルサイズが大きすぎます!<br>"; exit; } $imagesize = getimagesize($_FILES['img']['tmp_name']); switch($imagesize['mime']){ case 'image/gif': $ext = '.gif'; break; case 'image/jpeg': $ext = '.jpg'; break; case 'image/png': $ext = '.png'; break; default: echo $_FILES['img']['name']."は画像ファイル(GIF/JPEG/PNG) ではありません!!<br>"; exit; } //アップロード処理 $imageFileName = sha1(time().mt_rand()) . $ext; $imageFilePath = IMAGES_DIR.$imageFileName; $rs = move_uploaded_file($_FILES['img']['tmp_name'], $imageFilePath); if (!$rs) { echo '画像アップロードに失敗しました。'; exit; } header('Location: view.php'); exit; ?>
非公開ディレクトリより画像を読み込む
【view.php】
<?php require 'define.php'; $list = scandir(IMAGES_DIR); ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>SAMPLE</title> <style> img{ margin:0 5px 5px 0; max-width:160px; vertical-align:bottom; } table, td{ border-collapse: collapse; border:1px solid #333; } </style> </head> <body> <table> <tr><td>ファイル名</td><td> サイズ</td></tr> <?php foreach($list as $val) { if(($val == ".") || ($val == "..")){ continue; } $size = filesize(IMAGES_DIR. $val); ?> <tr><td><a href="img.php?img=<?=$val ?>"><img src="img.php?img=<?=$val ?>"></a></td><td> <?=$size?><br>bit</td></tr> <?php } ?> </table> </body> </html>
まず、読み込む画像ファイルの名前をgetパラメーターとして設定するために、非公開ディレクトリのファイル名一覧をscandir()で取得し変数に格納します。引数は、定数で設定した非公開ディレクトリのパスとなります。また、取得した変数には、Array ( [0] => . [1] => .. のように、[0]と[1]は表示に不必要なので、こちらは条件分岐により除外します。そして、取得した、一覧をループで表示してみます。もちろんここでの役割は、GETパラメーターとして、img.phpの方へ処理を投げることで、メインは、次に紹介するimg.phpでの処理となります。
【img.php】
<?php require 'define.php'; if (isset($_GET['img'])) { $FilePath = IMAGES_DIR. $_GET['img']; $FileName = $_GET['img']; } $type = image_type_to_mime_type(exif_imagetype($FilePath)); $file_length = filesize($FilePath); //Mime-Typeの取得 header('Content-Type: '.$type); //コンテントタイプ header('Content-Disposition: inline; filename="'.$filename.'"'); //DLの場合は、Content-Disposition: attachment; header("Content-Length:$file_length"); //ファイルサイズ readfile ($FilePath); //読み込み ?>
img.phpでは、view.phpより渡ってきたGETパラメーターをもとに指定の画像の読み込み処理をします。メインは、ヘッダーの設定で、まずは、header(‘Content-Type: xxxxxx’);の部分です。今回出力したいのは、アップロードの際に、指定した、jpeg,png,gifの3種類です。xxxxxxの部分には、直接書くこともできますが、1種類しか設定できないので、このあたりを関数を使って、出力したい画像に応じてそれぞれ取得します。まずは、xxxxxxxの部分を引数$typeとして、そこにMime-Typeを取得するようにします。exif_imagetype()関数の引数にファイルのパスを設定して、ファイルタイプの番号を取得します。次の、その番号を値をimage_type_to_mime_type()関数の引数として、Mime-Type を取得します。header(‘Content-Type: ‘.$type); これにより、GETパラメーターによってタイプを変更することが可能となます。
次に、header(‘Content-Disposition: inline; filename=”‘.$filename.'”‘); の部分では、Content-Disposition: inline; を指定します。これにより、アクセスがあった場合は、画像として表示されます。一方で、画像としての表示ではなく、ダウンロードさせたい場合は、Content-Disposition: attachment;を指定します。
そして、header(“Content-Length:$file_length”); でファイルサイズの指定です。この部分はなくても動きますが、ブラウザの解釈によっては、大きなファイルの場合、画像の読み込みに失敗するようなことがあるようなので、表示しておきます。