画像アップローダ

今回は画像アップローダを作ってみましょう。画像を単にアップロードするだけではなく、アップロードされた画像のサムネイルも作成します。アップロード用ディレクトリとして「photo」という名前のディレクトリをあらかじめ作っておき、パーミッションを「777」としておきます。なお、PHPにGDライブラリがインストールされている必要があります。phpinfo関数などで確認してみてください。

フォーム兼アップロード処理

今回のスクリプトは、アップロードのためのフォーム画面とアップロードのための処理および結果表示をひとつのスクリプトにまとめてみました。アップロードのためのフォームに「<input type="hidden" name="operation" value="upload" />」を挿入し、この値を基に処理を分岐します。

スクリプト

<?php
	$max = 10;
	if(!$_POST['operation']){
		echo'
			<form method="post" action="'.$_SERVER['PHP_SELF'].'" \
			enctype="multipart/form-data">
			<input type="hidden" name="operation" value="upload" />
		';
		for($i = 0; $i < $max; $i++){
			echo '<input type="file" name="userfile[]" size="60" /><br />';
		}
		echo'
			<input type="submit" value="アップロード" />
			</form>
		';
	}elseif($_POST['operation'] == 'upload'){
		$dir = 'photo/'.date('Ymdhis');
		umask(0);
		if(mkdir($dir, 0777)){
			for($i = 0; $i < $max; $i++){
				if(is_uploaded_file($_FILES['userfile']['tmp_name'][$i])){
					if($_FILES['userfile']['type'][$i] == 'image/jpeg' || \
					$_FILES['userfile']['type'][$i] == 'image/pjpeg'){
						$thumbname =  $dir.'/'.$i.'_thumb.jpg';
						$uploadfile = $dir.'/'.$i.'.jpg';
						if(move_uploaded_file($_FILES['userfile']['tmp_name'][$i], \
						$uploadfile)){
							chmod($uploadfile, 0644);
							$size = getimagesize($uploadfile);
							if($size[0] > $size[1]){
								$width = 100;
								$per = $size[0] / 100;
								$height = $size[1] / $per;
							}elseif($size['0'] == $size[1]){
								$width = 100;
								$height = 100;
							}else{
								$height = 100;
								$per = $size[1] / 100;
								$width = $size[0] / $per;
							}
							$new_image = imagecreatetruecolor($width, $height);
							$src_image = imagecreatefromjpeg($uploadfile);
							imagecopyresampled($new_image, $src_image, 0, 0, \
							0, 0, $width, $height, $size[0], $size[1]);
							imagejpeg($new_image, $thumbname, 75);
							if(file_exists($thumbname)){
								chmod($thumbname, 0644);
								imagedestroy($new_image);
								imagedestroy($src_image);
							}else{
								echo '<p>画像の出力に失敗しました</p>';
								imagedestroy($new_image);
								imagedestroy($src_image);
								break;
							}
						}else{
							echo '<p>アップロード失敗!</p>';
							break;
						}
					}else{
						echo '<p>アップロードできるファイル形式はJPEGのみです</p>';
						break;
					}
				}else{
					echo '<p>不正なアップロードです</p>';
					break;
				}
			}
			echo '<p>アップロード成功しました。</p>';
		}else{
			echo '<p>不正なアップロードです</p>';
			exit();
		}
	}
?>

解説

$max = '10';
一度にアップロードできる画像枚数を定義しています。今回は10枚としています。
if(!$_POST['operation']){ … }
グローバル変数「$_POST['operation']」に値がない場合の処理となります。ここでは画像をアップロードするためのフォームを出力しています。
$_SERVER['PHP_SELF']
自身のファイル名を取得するためのグローバル変数です。直接ファイル名を記述しておくのではなく、このように変数にしておくことで汎用性が高まります。
enctype="multipart/form-data"
ファイルをアップロードするフォームには、必ず「enctype="multipart/form-data"」を記述してください。
for($i = 0; $i < $max; $i++){ … }
変数「$max」に代入されている数だけ{ }内の処理が繰り返されます。この場合は「<input type="file" name="userfile[]" size="60" /><br />」が10回出力されます。userfile[]はアップロードした際に配列化され、通常の配列と同様にキーが与えられます。
elseif($_POST['operation'] == 'upload'){ … }
グローバル変数「$_POST['operation']」の値が「upload」の際に{ }内の処理が実行されます。すなわち、「アップロード」ボタンがクリックされたのちの処理ということになります。
$dir = date('Ymdhis');
画像をアップロードする際のディレクトリの名前を定義しています。今までアップロードした画像が上書きされないように、またアップロードされた日付がディレクトリ名から判断できるように処理実行時の年月日時分秒の値からディレクトリ名を決定し、その上位ディレクトリ「photo/」と結合させています。
umask(0)
unix系のサーバでは、その次に行の「mkdir($dir, 0777)」の処理を実行した際に、パーミッションが「755」となってしまうことがほとんどです。それは、unix系のサーバではデフォルトでumask値を持っていてその数値分をディレクトリなどを作成した際にパーミッションから差し引いてしまうからです。通常はumask値は「0022」です。この値分だけ差し引かれてしまうので、ディレクトリを作成した際にパーミッションが「755」になってしまいます。それを回避するためにumask関数でその値を「0」にしています。
if(mkdir($dir, 0777)){ … }
mkdir関数の実行が成功した際に{ }内の処理が実行されます。mkdir関数の引数は(ディレクトリ名,パーミッション)となっています。
for($i = 0; $i < $max; $i++){ … }
ここでも変数「$max」に代入されている数だけ{ }内の処理が繰り返されます。このように複数の箇所で同じ値が参照される場合はその値を変数に代入しておけば、一度にアップロードできる画像枚数を変更した際に、変数「$max」の値を1箇所変更するだけで済み、のちのメンテナンス作業を簡素化してくれます。
if(is_uploaded_file($_FILES['userfile']['tmp_name'][$i])){ … }
アップロードされたファイルが存在する場合に{ }内の処理を実行します。is_uploaded_file関数は引数のファイルがアップロードされているか否かを判断してするための関数です。引数の「$_FILE」は$_FILE[パラメータ名][一時的に保存されているパス][キー]となっています。
if($_FILES['userfile']['type'][$i] == 'image/jpeg' || $_FILES['userfile']['type'][$i] == 'image/pjpeg'){ … }
今回はアップロードできるファイルタイプをJPEGに限定しています。「$_FILES['userfile']['type']」でファイルのMIMEタイプを取得し、MIMEタイプがJPEGであれば{ }内の処理が実行されます。GIFファイルであればMIMEタイプは「image/gif」、PNGファイルであれば「image/png」となります。これで完全というわけではないのですが、不特定多数の人がアップロードできるようなサービスで使用する場合は(画像掲示板など)、ファイルタイプを限定し少しでもセキュリティを高めましょう。
$thumbname = $dir.'/'.$i.'_thumb.jpg'
サムネイルのファイル名を定義しています。
$uploadfile = $dir.'/'.$i.'.jpg'
アップロードされたファイルのファイル名を定義しています。
if(move_uploaded_file($_FILES['userfile']['tmp_name'][$i], $uploadfile)){ … }
move_uploaded_file関数で一時的に保存された画像ファイルを実際の保存場所に移動します。引数は(一時的に保存されたファイル,実際の保存場所)となり、move_uploaded_file関数が正常に実行されれば、{ }内の処理を実行します。
chmod($uploadfile, 0644)
chmod関数で、保存されたファイルのパーミッションを「644」にしています。
$size = getimagesize($uploadfile)
getimagesize関数で保存されたファイルのサイズを取得し、「$size」に格納しています。getimagesize関数の戻り値は配列となり、キー「0」は幅のピクセル数、キー「1」は高さのピクセル数となります。
if($size[0] > $size[1]){ … }
今回のサムネイルは長辺が100pxのものを作成します。「$size[0] > $size[1]」なので画像が横長の場合の処理となります。
$width = 100
横長の画像なので変数「$width」に横幅のピクセル数100を代入しています。
$per = $size[0] / 100
「$size[0](横幅) / 100」で横幅の縮小比を計算しその解を変数「$per」に代入しています。
$height = $size[1] / $per
横幅と同じ割合で高さを縮小しています。これで縦横比が維持されたサムネイルのサイズが計算されます。
$new_image = imagecreatetruecolor($width, $height)
上記if構文内にて計算した縦横のサイズで黒いTRUECOLORイメージを作成し、変数「$new_image」とします。
$src_image = imagecreatefromjpeg($uploadfile)
アップロードしたファイルからサムネイルを作成するためのソースファイルを作成し、変数「$src_image」とします。
imagecopyresampled($new_image, $src_image, 0, 0, 0, 0, $width, $height, $size[0], $size[1])
imagecopyresampled関数は画像をコピーして伸縮を行うための関数です。画像が滑らかになるようピクセル値も補間してくれますので、割と綺麗にサイズを変更してくれます。引数は(コピー先画像リソース,コピー元画像リソース,コピー先のx座標,コピー先のy座標,コピー元のx座標,コピー元のy座標,コピー先の幅,コピー先の高さ,コピー元の幅,コピー元の高さ)になります。
imagejpeg($new_image, $thumbname, 75)
imagejpeg関数でimagecopyresampled関数から作成した画像をJPEGに出力しています。引数は(画像リソース,ファイルの保存先のパス,画像クオリティ(オプション・0〜100でデフォルトが75))になります。これでサムネイルは完成したことになります。
if(file_exists($thumbname)){ … }
file_exists関数でサムネイルの存在を確認できた場合{ }内の処理を実行します。
imagedestroy($new_image)
imagedestroy関数でサムネイルを生成する際に作成した画像リソースを破棄します。
break
ループを脱出するための制御構造です。ここでは何らかの理由で処理が失敗してしまったときに以降の処理が繰り返されないよう使用しています。

▲ページトップに戻る