あーる学習帳

自分が勉強したことや気になることなど、99%自分用です。コードを書いてるのでPCから閲覧を推奨。

PHP実習:PHPで画像データ送信

ブログの記事タイトルもこんな風にしていきます。

googleサイトが気になるところ。あれ使えば会社で予定管理や案件管理ができるポータルサイトが作れるって読んだので…。

 

第8章-04

画像データをアップロードするためのページを作っていく。

<html>
 <body>
  <h1>画像アップロード</h1>
  <form action="pic_receive.php" method="POST" enctype="multipart/form-data">
   <p><input type="file" name="img"></p>
   <p><input type="submit" value="送信"></p>
  </form>
 </body>
</html>

f:id:R_de_aru:20180628203557p:plain

POSTデータとして画像を送信する。formタグで「enctype="multipart/form-data"」と指定することにより、データを配列で送信することができる。フォーム内に画像データが含まれる場合は必ず指定する。そしてinputタグのtypeにfileを指定することにより、ボタンをクリックすると画像の選択画面が現れる。

f:id:R_de_aru:20180628203817p:plain

画像ファイルは専用のディレクトリを作って管理するので、index.phpがあるフォルダに「img」というフォルダを作っておく。受信ページを作成していく。

 <?php
 $err=array();
 $img=$_FILES['img'];
 var_dump($img);
 move_uploaded_file($img['tmp_name'],'./img/'.$img['name']);
?>
<html>
 <head>
  <meta charset="utf-8">
 </head>
 <body>
  <h1>受信ページ</h1>
  <?php
   if (count($err)>0) {
    foreach ($err as $row) {
    echo '<p>'.$row.',</p>';
   }
   echo '<a href="./index.php">戻る</a>';
   } else {
  ?>
  <div><img src="ttp://192.168.33.11:8000/img/<?php echo $img['name'];?>"></div>
  <?php } ?>
 </body>
</html>

この状態で画像をアップしてみる。今回は大好きな仮面ライダーアマゾンオメガの凛々しいお姿をS.H.フィギュアーツのページからお借りしました(権利的な問題があれば即刻消します)。

※ソース内のURLについて、最初のhを消してます

f:id:R_de_aru:20180628214455p:plain

上手くアップされた。

画像のデータは$_FILESというスーパーグローバル変数で取得する。キー名とデータの内容は以下の通り。

name...ファイル名

type...ファイルのMIMEタイプ

tmp_name...サーバー上で一時的に保管されるテンポラリーファイル名

error...アップロード時のエラーコード

size...ファイルサイズ(バイト) 

送信ボタンを押したとき、画像そのもののデータは一時保管場所に預けられる。$_FILESに格納されるのは画像データそのものではなく、画像に付随するデータである。

$_FILES['img']['name']にはもともとのファイル名が格納される。typejpegpngなど画像の種類tmp_nameはtmpフォルダにできたファイル名となる。errorは0が返ってくれば成功、1はファイルサイズのオーバー、7は書き込み失敗を意味する。

一時的にサーバー上のディレクトリにアップロードされたファイルは公開フォルダに移動する必要がある。作成したimgフォルダへの移動にはmove_uploaded_file()という組み込み関数を使う(第1引数は現在地、第2引数は移動先)。ファイル名も一情報の一部とみなされるので、必ず「ディレクトリ名/ファイル名」まで指定すること。これにより、imgフォルダに画像ファイルが移動する。

これでアップロード機能自体は完成しているが、画像ファイルはデータ量が多いのでサーバーの負担になることがある。そもそも画像ファイルが悪意のあるコードを含んだ偽りのファイルである可能性も否めない。そのため、バリデーション機能は必須と言える。さらに、本来画像ファイルはそのままの名前では保存しない。名前を変換してから保存するようにコードを追加・修正していく。

<?php
 $err=array();
 $img=$_FILES['img'];
 var_dump($img);

 $type=exif_imagetype($img['tmp_name']);
 if ($type!==IMAGETYPE_JPEG&&$type!==IMAGETYPE_PNG) {
  $err['pic']='対象ファイルはPNGまたはJPEGのみです。';
 }elseif ($img['size']>102400) {
  $err['pic']='ファイルサイズは100KB以下にしてください。';
 }else {
  $extension=pathinfo($img['name'],PATHINFO_EXTENSION);
  $new_img=md5(uniqid(mt_rand(),true)).'.'.$extension;
 }
 move_uploaded_file($img['tmp_name'],'./img/'.$new_img);
?>

exif_imagetype()によってファイルの形式を取得する($_FILES['img']['type']で検証しないこと)。exif_imagetype()では整数が返ってくる(2:JPEG、3:png)が、後から読むときにわかりづらいのであらかじめ定義されているIMAGETYPE_JPEGやIMAGETYPE_PNGを活用する。ここでは100KB(=102400バイト)よりファイルサイズが大きいものはエラーとする。

ファイル名は元のものを使わないことが一般的。同じ名前でアップロードされたときに上書きされてしまうことへの対処、「phpinfo.php.png」というような多重拡張子を防ぐなどの理由がある。pathinfo($img['name'],PATHINFO_EXTENSION)でファイル名のドット以降の文字を取得する。md5(uniqid(mt_rand(),true))で乱数を作って限りなく重複する可能性が少ない名前を作り、それに取得した拡張子名をつなげる。

php.iniを変更すれば、アップロードできるファイルのデータサイズやファイル数などを設定できる。

 

この勢いで実習まで進みたかったけど、夜も遅くなってきたので今日はここまで。実は途中で詰まってしまってなかなか進めなかったのです。原因が「move_uploaded_file」と書くべきところを「move_upload_file」と書いていたことで、気付くのに30分くらいかかった…。