phpのフォームでファイルのアップロードをする2

phpでファイルをアップロードするときのやり方の続きです。
前回はアップロードする際の、送り出すHTMLを説明しましたが、今回は受け取る方のphpの説明です。

upload.php
HTMLのヘッダ、bodyは省略してます。

<?php
if(!isset($_FILES['my_file'])){
	echo 'ページ遷移が不正です。';
}else{
	if($_FILES['my_file']['error'] !== UPLOAD_ERR_OK){
		
		//エラーが発生している
		if($_FILES['my_file']['error'] == UPLOAD_ERR_FORM_SIZE){
			echo 'ファイルサイズがHTMLで指定した MAX_FILE_SIZE を超えています。';
		}elseif($_FILES['my_file']['error'] == UPLOAD_ERR_NO_FILE){
			echo 'ファイルが選択されていません。';
		}else{
			echo 'その他のエラーが発生しています。';
		}
		
	}else{
		
		//ここから通常の処理

		//ユーザーが指定したファイル名
		$myfile_name = $_FILES['my_file']['name'];
		//ファイルのMIME型
		$myfile_type = $_FILES['my_file']['type'];
		//ファイルサイズ
		$myfile_size = $_FILES['my_file']['size'];
		//アップロードしたファイルが保存されている一時保存場所
		$myfile_tmp_path = $_FILES['my_file']['tmp_name'];
		
		//SQLインジェクション対策用
		$safesql_myfile_name = mysql_real_escape_string($myfile_name);
		$safesql_myfile_type = mysql_real_escape_string($myfile_type);
	
		//HTML表示用
		$safehtml_myfile_name = htmlspecialchars($myfile_name);
		$safehtml_myfile_type = htmlspecialchars($myfile_type);
		
		
		//拡張子の取得
		$tmp_ary = explode('.',$myfile_name);
		if(count($tmp_ary)>1){
		    $extension = $tmp_ary[count($tmp_ary)-1];
			
			//拡張子が半角英数字以外なら拡張子がないものとする。
			if( !preg_match("/^[0-9a-zA-Z]+$/",$extension) ) $extension='';
		}else{
			//拡張子がない場合はそのまま。Macなど。
			$extension='';
		}
		
		//SQLインジェクション対策用
		$safesql_extension = mysql_real_escape_string($extension);
		//HTML表示用
		$safehtml_extension = htmlspecialchars($extension);
			
		
		//新しいファイル名を作成する
		$new_file_name = date("Ymd-His").'-'.mt_rand().'.'.$safehtml_extension;
		
		//php側でもファイルサイズのチェックを行う。
		if($myfile_size>10485760 or $myfile_size==0)die('エラー ファイルサイズが不正です。');		

		//ファイルの保存場所
		$myfile_new_path = '/uploads';
		if(!move_uploaded_file($myfile_tmp_path,"$myfile_new_path/$new_file_name")){
			die('エラー ファイルを保存できませんでした。');
		}
		
		echo 'アップロードは成功しました。<br /><br />';
		
		echo 'ファイル名 : '.$safehtml_myfile_name.'<br />';
		echo 'MIME型 : '.$safehtml_myfile_type.'<br />';
		echo 'ファイルサイズ : '.number_format($myfile_size).' bytes<br />';
		echo '新しいファイル名 : '.$new_file_name.'<br />';

	}
}
?>

サンプル

パーツの説明

1.POSTの存在確認

if(!isset($_FILES['my_file'])){
	echo 'ページ遷移が不正です。';
}

ファイルの存在の確認をしています。HTTP POST で渡された$_FILESの存在を確認します。
連想配列の名前は前回 input で指定した名前になります。

<input type="file" name="my_file" />

今回のサンプルの場合、POSTされずに来るページではないので、die()しても構わないと思います。

2.エラーの確認

大まかなエラーの確認からしていきます。

if($_FILES['my_file']['error'] !== UPLOAD_ERR_OK){

$_FILES[‘my_file’][‘error’] で自動的にエラーを確認してくれるので楽チンです。
エラーがない場合は UPLOAD_ERR_OK もしくは 0 。ある場合はエラーコードを返します。

if($_FILES['my_file']['error'] == UPLOAD_ERR_FORM_SIZE){

UPLOAD_ERR_FORM_SIZE 。もしくは 2 。そのままですが、ファイルサイズがHTMLで指定した MAX_FILE_SIZEを超えた場合の値。MAX_FILE_SIZEはユーザーが自由に変更できるので、あくまでも目安に。

}elseif($_FILES['my_file']['error'] == UPLOAD_ERR_NO_FILE){

同じくそのまま。値は 4 。ファイルが指定されていないです。
【1.存在確認】と違い、間違って押してしまう場合があるので、明示してあげるのが親切。

}else{
    echo 'その他のエラーが発生しています。';
}

上記2つ以外にもエラーコードがありますが、頻度が少ないので一括で処理。
その他のエラーコードはマニュアルを見て下さい。

3.$_FILESで取得できる値

$myfile_name = $_FILES['my_file']['name'];
$myfile_type = $_FILES['my_file']['type'];
$myfile_size = $_FILES['my_file']['size'];
$myfile_tmp_path = $_FILES['my_file']['tmp_name'];

それぞれファイル名、MIMEタイプ、サイズ、一時保存場所です。

$safesql_myfile_name = mysql_real_escape_string($myfile_name);
$safesql_myfile_type = mysql_real_escape_string($myfile_type);
$safehtml_myfile_name = htmlspecialchars($myfile_name);
$safehtml_myfile_type = htmlspecialchars($myfile_type);

SQLインジェクションやXSS対策のために汚染されていない値を作っておく。
ファイル名、MIMEタイプは汚染されている可能性があります。
サイズ、一時保存場所の値は安全ですが、念のため同様にサニタイズした方がいいと思います。
→一時保存場所は /var/phptmp など。サイズは整数値。
またSQLに関しては必ずプリペアドステートメントをするべきです。

4.拡張子の取得

いろいろな方法があると思いますが、ここでは[ . ](ピリオド)で分割し、配列の最後の値(-1)を取得しています。

$tmp_ary = explode('.',$myfile_name);
if(count($tmp_ary)>1){
    $extension = $tmp_ary[count($tmp_ary)-1];
}else{
    $extension='';
}

MACなどで拡張子がない場合もありますので、ピリオドがない場合は拡張子なしとしています。

if( !preg_match("/^[0-9a-zA-Z]+$/",$extension) ) $extension='';

拡張子が半角英数字以外は拡張子をないものとしてます。
MACで拡張子がなく、[ プレゼン資料.2013.04.02修正バージョン ]のようなファイル名も考えられ、これに対応します。ここで拡張子の桁数を確認してもいいですが、拡張子はシビアになる項目でもないです。
(そもそもユーザー指定なので信用せず単なる目安と考える)

$safesql_extension = mysql_real_escape_string($extension);
$safehtml_extension = htmlspecialchars($extension);

ユーザーから送信された拡張子は安全ではないのでサニタイズしています。

5.新しいファイル名の作成

ユーザー指定のファイル名だと何かと不都合が多いので、サーバー側で指定します。
ユーザー指定だと保存フォルダがカオスになり、当然重複も発生します。

$new_file_name = date("Ymd-His").'-'.mt_rand().'.'.$safehtml_extension;

ファイル名には時間を指定していますが、全く同時にアップロードも考えられますので、ランダムな値を付加しています。マイクロ秒などでもほとんど問題ないと思いますが、ランダムな値のほうがお勧めです。
拡張子は$safehtml_extensionを利用しました。

※マイクロ秒などでは操作によって同時が考えられる。

6.ファイルサイズのチェック

if($myfile_size>10485760 or $myfile_size==0)die('エラー ファイルサイズが不正です。');

php側でもファイルサイズのチェックは必須です。
MAX_FILE_SIZEの値は信用出来ません。
また、念のためファイルサイズが0の場合もチェックしています。

7.ファイルの移動

if(!move_uploaded_file($myfile_tmp_path,"$myfile_new_path/$new_file_name")){
	die('エラー ファイルを保存できませんでした。');
}

move_uploaded_fileで一時保存場所からファイルを移動します。
アップロードするファイルの場所はフルパスで指定したほうがいいです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です