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>
POSTデータとして画像を送信する。formタグで「enctype="multipart/form-data"」と指定することにより、データを配列で送信することができる。フォーム内に画像データが含まれる場合は必ず指定する。そしてinputタグのtypeにfileを指定することにより、ボタンをクリックすると画像の選択画面が現れる。
画像ファイルは専用のディレクトリを作って管理するので、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を消してます
上手くアップされた。
画像のデータは$_FILESというスーパーグローバル変数で取得する。キー名とデータの内容は以下の通り。
name...ファイル名
type...ファイルのMIMEタイプ
tmp_name...サーバー上で一時的に保管されるテンポラリーファイル名
error...アップロード時のエラーコード
size...ファイルサイズ(バイト)
送信ボタンを押したとき、画像そのもののデータは一時保管場所に預けられる。$_FILESに格納されるのは画像データそのものではなく、画像に付随するデータである。
$_FILES['img']['name']にはもともとのファイル名が格納される。typeはjpegやpngなど画像の種類。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分くらいかかった…。