PHPで複数ファイルをアップロードするサンプル
前回の記事「PHPでファイルをアップロードするサンプル」の続きのようなもので、今回は1つではなく複数のファイルをアップロードするサンプルです。
PHPで複数ファイルをアップロードする処理
今回もサンプルをいきなりドン!
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body>
<h1>アップロード処理のサンプル</h1>
<form enctype="multipart/form-data" method="post" onsubmit="return checkForm();">
<input type="hidden" name="MAX_FILE_SIZE" value="1000000">
<input name="file[]" type="file" multiple="multiple">
<input type="submit" name="_upload" value="アップロード">
</form>
<?php
//[アップロード]ボタンの押下確認
if (isset($_POST['_upload'])) {
foreach ($_FILES['file']['tmp_name'] as $no => $tmp_name) {
//ファイルをテンポラリから保存場所へ移動(但し本来は渡されたファイル名をそのまま使うのは危険)
$filename = './'.$_FILES['file']['name'][$no];
if (move_uploaded_file($tmp_name, $filename)) {
echo $_FILES['file']['name'][$no].'をアップロードしました<br>';
} else {
//エラー処理
}
}
}
?>
<script>
function checkForm()
{
if (document.getElementsByName('file[]')[0].value == '') {
alert('ファイルを選択してください');
return false;
}
return true;
}
</script>
</body>
</html>
解説
前回の記事から変わった部分のみ解説します。
HTML部分
前回)<input name="file" type="file">今回)<input name="file[]" type="file" multiple="multiple">
inputタグにmultiple属性を設定することで、ブラウザのダイアログで複数のファイルが選択できるようになります。また、それに伴い、複数のファイル情報を連想配列へ格納するためにname属性をfileからfile[]という表記へ変更しています。
同様に、JavaScript側も下記のように変更されています。
前回)if (document.getElementsByName('file')[0].value == '') {
今回)if (document.getElementsByName('file[]')[0].value == '') {
PHP部分
単一ファイルだったときは連想配列の
・$_FILES['file']['tmp_name'] にテンポラリファイル名
・$_FILES['file']['name'] に選択されたファイル名
が入っていました。
しかし、今回は複数になるため、例えば2つのファイルを選択された場合は、
・$_FILES['file']['tmp_name'][0] に1つ目のテンポラリファイル名、
・$_FILES['file']['tmp_name'][1] に2つ目のテンポラリファイル名、
という感じになります。
よって、
foreach ($_FILES['file']['tmp_name'] as $no => $tmp_name) {
//省略
}
というように foreach で選択されたファイル数分ループしているわけですね。($no には0~連番で数字が入ります。)
ちなみに、ファイルを1つだけ選択された場合も $_FILES['file']['tmp_name'] は配列になっているため、$_FILES['file']['tmp_name'][0] にテンポラリファイル名が入ってきます。
あとの is_uploaded_file や move_uploaded_file は前回と同様です。
HTML側でファイルの種類を”一応”指定できる
余談ですが、inputタグにaccept属性を設定することで、画像ファイルのみを選択させたりすることも一応できます。
例)
<input name="file[]" type="file" multiple="multiple" accept="image/*">
こうすることで、ブラウザのファイル選択ダイアログで「画像ファイル」のみ選択できるようになります。なぜ”一応”と書いたのかというと、ユーザー側でダイアログのプルダウンを操作して「画像ファイル」から「すべてのファイル」へ変更できるからです。これはChromeでもEdgeでもIEでも同様です。
つまり、accept="image/*" を指定してもZIPファイルとかをアップロードされる可能性はあるわけですね。
なので、アップロードされるファイルの種類を制限したい場合はJavaScriptで拡張子のチェックをし、アップロード処理をするPHP側でもチェックするべきです。
(JavaScriptを通さずにPHPにPOSTすることもできるため)
まとめ
(単一ファイルのアップロード処理は理解していると仮定して)複数ファイルのアップロードに対応する手順はおおよそ下記のとおりです。
- inputタグにmultiple属性を追加
- inputタグのnameを連想配列用 file[] に変更
- PHP側では $_FILES['file']['tmp_name'] が配列として入ってくるためループして処理を行う
前回の記事でも書いたとおり、ファイルのアップロードは適当に書いただけでは脆弱性だらけになってしまいます。全部書くととても長くなるので、拡張子、ファイルサイズ、ファイルタイプのチェックなどはまた別の機会に解説したいと思います。