[Javasscript] input type=fileのmultipleをformDataで送信する時のお作法

2023年12月21日

Javascript

eyecatch Webシステムでファイルアップロードシステムを作る場合、JavascriptからPHPへ送信するのが便利でよくその構成で構築します。 通常は、1ファイルずつの操作で行いますが、今回は複数ファイルをまとめて操作する必要が出てきていつものように構築してみたところ、 何故かサーバー側で複数のファイルをアップしても1つしか受け取れないという事象が発生しました。 原因はホントに凡ミスだったですが、自分への戒めのためにブログに残しておきたいと思います。 ついでに、Javascriptで、プレビュー表示しながら、PHPにform送信せずにアップロードするコードも載せておきます。

原因と対策

今回の不具合の原因は、以下のHTMLコードが原因でした。 <input type='file' name='file' multiple/> ちゃんと複数ファイルが選択できるように、"multiple"属性がついているところがポイントですね。 ファイルを利用者のパソコン内からピックアップしてもらうために、inputタグのtype="file"機能を使います。 この場合、PHPに送る場合にname="file"の箇所を、name="file[]"と配列型にしてあげないと、単数扱いになって複数送られても、1つの変数で上書きされてしまうという事象でした。

サンプルコード

ちなみに、上記のname="file"でも、javascriptでデータを整形し直して送ることもできるので、できればjavascriptでコントロールできるようになっておいたほうがいいかと思います。 今回のコードをHTMLを修正せずに対応するサンプルコードは以下のとおりです。

upload.html

<form method='post' name="upload" enctype='multipart/form-data'> <input type='file' name='file' multiple/> </form>

upload.js

function set_event(){ const input_file = document.querySelector(`input[name="file"]`) if(input_file){ input_file.addEventListener("change" , change_input_file) } } function change_input_file(e){ const formData = new FormData(document.forms.contact) // 複数アップロードファイルに対応 for(const file of e.target.files){ formData.append("file[]" , file) } const req = new XMLHttpRequest(); req.open("POST", "upload.php" , true); req.send(formData); } switch(document.readyState){ case 'complete': case 'interactive': set_event() break default: window.addEventListener('DOMContentLoaded', (()=>set_event())) break }

upload.php

<?php if(!is_query_file()){ $res_data = [ "status" => "error", "datas" => [], "message" => "no-query", ]; exit(); } make_file_dir(); save_files(); echo json_encode($res_data , JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); function is_query_file(){ return isset($_FILES['file']) ? true : false; } function save_files(){ $uuid = uniqid(); $res_datas = []; for($i=0; $i<count($_FILES['file']['name']); $i++){ $save_file = save_file($uuid , $i , [ "name" => $_FILES['file']['name'][$i], "full_path" => $_FILES['file']['full_path'][$i], "type" => $_FILES['file']['type'][$i], "tmp_name" => $_FILES['file']['tmp_name'][$i], "error" => $_FILES['file']['error'][$i], "size" => $_FILES['file']['size'][$i], ]); array_push($res_datas , [ "base" => $_FILES['file']['name'][$i], "size" => $_FILES['file']['size'][$i], "path" => $save_file, ]); } return $res_datas; } function get_file_dir(){ return "files/"; } function make_file_dir(){ $dir = get_file_dir(); if(is_dir($dir)){return;} mkdir($dir , 0777 , true); } function get_ext($filename=null){ $sp = explode(".", $filename); return implode(".", array_slice($sp , -1)); } function save_file($uuid=null, $num=null, $file=[]){ $ext = get_ext($file["name"]); $new_file = $uuid ."_". $num.".".$ext; $new_path = get_file_dir().$new_file; move_uploaded_file($file["tmp_name"], $new_path); }

あとがき

Webページからファイルのアップロードが柔軟にできるようになると、だいたいどんなシステムでも構築するのが楽になりますね。 ちなみに、アップロードする前に、画像ファイルであれば、Javascriptで画像をプレビュー表示するような仕様を間に挟むと、めちゃくちゃUIがイケてるシステムになること請け合いですね。 あと、PHPの送信をしたあと、アップロードしたデータ(ファイル)の詳細などをjson形式でレスポンスしたり、スマホなどで撮影した画像ファイルなどは大きすぎるので、PHP側で縮小処理をしたりすることで、かなり安定したシステムになります。 その辺は、構築するシステムに合わせた形で盛り込んでいくのが良いと思います。 自分も今後は、事あるごとに、このページのソースをコピペして使い続けるとと思います。