PHPでファイルをアップロードする方法【サンプルコードで解説】
PHPでファイルのアップロードの方法をサンプルコードでまとめました。
今回の解説の際に使用するファイル・フォルダ構成
今回は下記のファイルとフォルダの構成で、画像ファイルをアップロードについて解説します。
- img(画像保存ディレクトリ)
- image_form.php(送信フォーム)
- image_post.php(受け取り先)
アップロードの流れは以下の通りに行います。
- image_form.phpのフォームで送信画像を設定
- image_post.phpで画像をimgディレクトリに格納
- 最後にimgディレクトリに送信された画像が存在するか確認
送信フォームの設定(image_form.php)
画像の送信は、formタグのPOST送信を行います。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>画像送信テスト</title> </head> <body> <form action="image_post.php" method="POST" enctype="multipart/form-data"> <p> <input type="file" name="image"> </p> <p> <input type="submit" value="送信する"> </p> </form> </body> </html>
※上記ソースコードの表示例です。この画面では送信できません。
enctype="multipart/form-data"について
ファイルのアップロードには、fromタグの属性に「enctype="multipart/form-data"」を設定する必要があります。
これは、「画像などのファイルも送信しますよ」を明示するための設定なので、ファイルを送信するためには必須の記述です。
送信先の設定(image_post.php)
$_FILESの仕様を知ろう
input type="file"で送信が成功した場合、ファイルは$_POSTや$_GETではなく、$_FILESというグローバル変数に格納されます。
では実際に、image_form.phpで送信された$_FILESの中身をprint_rで確認してみましょう。
print_r($_FILES);
すると、$_FILESの配列の構成が確認できます。
以下は実際にサンプルで送ってみた$_FILES配列の中身です。
Array
(
[image] => Array
(
[name] => fruit_banana.png
[type] => image/png
[tmp_name] => /tmp/php4fk4aZ
[error] => 0
[size] => 23673
)
)
$_FILES[]のキーは、<input type="file" name="image">で指定したname属性で受け取ります。
今回はnameを「image」で設定したため、取得の際は$_FILES['image']['name']とすることで配列の値にアクセスすることが出来ます。
そして、$_FILES['image']に格納されている配列毎のデータは、以下のような形で構成されています。
name | 送信された元のファイルの名前を受け取ります。 |
---|---|
type | ファイルのタイプ(種類)が格納されます。 |
tmp_name | 送信されたファイルが一時保存されている(テンポラリファイル)場所を指します。 image_post.phpの処理が終了すると消去されます。 |
error | ファイル送信時のエラーチェックです。 0であれば成功で、その他はエラー内容に応じて数値が変化します。
|
size | 送信されたファイルサイズです。 |
move_uploaded_file()で送信したファイルを保存先に移動させよう
前述の通り、フォームで送信したファイルはテンポラリフォルダというディレクトリに一時的に保存され、プログラムが終了されるとファイルは破棄されます。
なので、送信によって一時保存されたファイルをmove_uploaded_file()で指定のディレクトリに移動させる方法が、一般的なファイルのアップロード方法となります。
//ファイルが送信されていない場合はエラー処理 if(!isset($_FILES['image'])){ echo 'ファイルが送信されていません。'; exit; } //ファイル名を使用して保存先ディレクトリを指定 basename()でファイルシステムトラバーサル攻撃を防ぐ $save = 'img/' . basename($_FILES['image']['name']); //move_uploaded_fileで、一時ファイルを保存先ディレクトリに移動させる if(move_uploaded_file($_FILES['image']['tmp_name'], $save)){ echo 'アップロード成功!'; }else{ echo 'アップロード失敗!'; }
「アップロード成功!」とechoされていれば、ファイル保存は成功されていると思われます。
最後に、実際にファイルがアップロードされたか、「img」フォルダを見て確認しておきましょう。
basename()とは?ディレクトリを跨がせないように処理しよう
サンプルのソースコードには、保存先の設定時にbasename()という関数を使用しています。
basename()は、例えば「../../test.png」というファイル名のファイルを受け取った時、「test.png」という文字列に変換します。
要するにパスを排除し、ファイル名のみ切り取る関数、ということです。
basenameがないと、本来の意図した保存先ではない場所に保存されたり、ファイルを上書きされてしまう「ディレクトリトラバーサル攻撃」を受ける可能性があります。
なので、不特定多数のユーザーからアップロードを受け付ける場合は、basenameの必ず噛ませたり、ファイル名をそのまま使用しない等のセキュリティ対策を取りましょう。
move_uploaded_fileは権限が必要なので注意
move_uploaded_file(保存ディレクトリ): failed to open stream: Permission denied in~
というエラーが出る場合は、imgフォルダ、つまり保存先フォルダのパーミッション(権限)が原因です。
フォルダの権限を変えられる、FTPツールなどでパーミッションを「777」に設定して再度試してみましょう。
僕が使用しているWinSCPというFTPツールでは、「右クリック→プロパティ」から編集が出来ます。
他のFTPツールでも同様の方法で権限を変更できるはずなのでお試し下さい。
複数ファイルを一括でアップロードする方法
複数ファイルをアップロードしたい場合は、name属性の末尾に[]を付けて、配列形式で送信できるようにします。
また、multipleという属性を付けて、ファイル選択時に複数選択できるようにします。
<input type="file" name="image[]" multiple>
画像選択時に、Ctrlキー(MacはShiftキー)を押しながら画像をクリックすることで複数選択できます。
スマホの場合は画像長押しで複数選択となります。(対応していないブラウザもあるため注意)
複数ファイル送信後に入る$_FILES配列の形式はこの通り。
Array
(
[image] => Array
(
[name] => Array
(
[0] => fruit_banana.png
[1] => fruit_ichigo.png
[2] => fruit_sudachi.png
)
[type] => Array
(
[0] => image/png
[1] => image/png
[2] => image/png
)
[tmp_name] => Array
(
[0] => /tmp/phpuyiZAS
[1] => /tmp/phpuyiRGP
[2] => /tmp/phpuyiLKQ
)
[error] => Array
(
[0] => 0
[1] => 0
[2] => 0
)
[size] => Array
(
[0] => 23673
[1] => 33133
[2] => 33515
)
)
)
ファイル毎に配列形式で受け取るため、foreachで繰り返し処理を行い、すべてのファイルの保存が出来るようにします。
参考 PHPのforeach文を徹底解説【サンプルコード有】
//ファイルが送信されていない場合はエラー処理 if(!isset($_FILES['image'])){ echo 'ファイルが送信されていません。'; exit; } //ファイルの繰り返し処理 foreach($_FILES['image']['error'] as $key => $error){ if($error) continue; //送信エラーの場合はブロック $save = 'img/' . basename($_FILES['image']['name'][$key]); move_uploaded_file($_FILES['image']['tmp_name'][$key], $save); }
後はimgフォルダを確認して、アップロードされていれば処理成功です。
以上、PHPでファイルをアップロードする方法、でした。
ディスカッション
コメント一覧
まだ、コメントがありません