- ベストアンサー
WordPressでのファイルアップロードの選択肢
- WordPressでFetch APIを利用してファイルをアップロードする方法が困難であることに悩んでいる。
- Ajaxを使ったファイルアップロード方法は多く存在するが、データベース保存が見つからず、サーバーディレクトリ保存が推奨される。
- 適切なアップロード方法を選定する際に、Fetch APIの利用可能性が判断基準となる。
- みんなの回答 (39)
- 専門家の回答
質問者が選んだベストアンサー
・ローカルストレージなどに保存しないと入力内容が消えるのではないかというアドバイス これは私は疑問に感じています。 消えるのかどうかわからないです。 どちらかと言うと消えないと思っています。 必ず消えるとかでないのなら、消えた場合に入力画面に戻るようにして、再入力してもらえばいいと思います。 消えないと言うか消えてもいいと言うか、セッションならこれは問題になりません。 私が想像しているセッションを使う場合の補足ですが、セッションは確定した登録用のものです。 表示切り替えはクライアント側のデータをそのまま使いまわします。 なのでサーバー側(セッション内容)とクライアント側のデータに相違があることがあるかもしれません。 それは確認画面まで進んでクライアント側のデータが改ざんされた場合ですが、それは無視します。 安全で確定したセッション内容を優先するからです。 そうすれば、確認画面から結果画面へ行く時は、「確認画面から結果画面へ行くよ」と言う情報しか(改ざんされたデータがない)サーバーに送信しないので安全です。
その他の回答 (38)
- dell_OK
- ベストアンサー率13% (766/5720)
私なりに非同期サンプルの質問画面を作ってみました。 http://oksample.starfree.jp/%E8%B3%AA%E5%95%8F%E6%8E%B2%E7%A4%BA%E6%9D%BF/ おおまかに操作感を確認するためのものです。 画像アップロードや回答画面は実装していません。
補足
サンプルページありがとうございます、入力画面に戻る際に入力した内容が保存されているところを不思議に思ったのですが、セッションで値を引き継いでいるのでしょうか? もう1点お聞きしたいのですが、コメントを送信後に表示画面で入力画面と同じものを表示することは可能でしょうか? コメントを複数投稿したい方がいた場合、ページを再度読み込みせずに投稿できるように作成したいと考えております。
- dell_OK
- ベストアンサー率13% (766/5720)
・意図が分からず そうなんですよ。 私も意図がわからないので、ひとまずこの意見は無視していいかなと思ったりもしました。 意図が分からないまま実装しても、果たしてそれが正しいかどうかはわからないのです。 その人になぜそうする必要があるのかを聞いた方がいいかも知れないですね。 「(ここで非同期 GET 送信して正常性確認した結果も書くと親切)」 これはなにをどうしたらいいのかもわかりません。 親切ってだけなら、いらないのでしょうけれども。
補足
回答ありがとうございます、アドバイス頂いた方にお聞きしてみます。
- dell_OK
- ベストアンサー率13% (766/5720)
・非同期通信は2度必要なようです… 私はそうは思いませんが、質問者さまがそうしたいのであればそれでかまいませんのでそれで進めていきましょう。
補足
A.回答ありがとうございます。 アドバイス頂いたように組んでいたのですが、意図が分からずどのように実装すれば良いかアドバイスお願い致します。 非同期 GET 送信して正常性確認した結果も書く ← これは何をすれば良いのか分かりますでしょうか? sample.php が最新コードになります。 ※頂いたアドバイス → 画面内に、回答の確認フォームを追加 → 入力フォームから確認したい内容を確認フォームに書き込み (ここで非同期 GET 送信して正常性確認した結果も書くと親切) ※最新コード https://wandbox.org/permlink/LNuot040YUwMX0dP
- dell_OK
- ベストアンサー率13% (766/5720)
sample.phpを見てみました。 セッションは使いませんので以下の部分は不要です。 ---- $attach = []; if (!empty($_SESSION['attach'])) { foreach ($_SESSION['attach']['data'] as $i => $data) { if (!empty($data)) { $base64 = base64_encode($data); } $type = $_SESSION['attach']['type'][$i]; switch ($type) { case 'image/jpeg': case 'image/png': $attach[] = '<img style="height: 100px;" src="data:'.$type.';base64,'.$base64.'">'; break; case 'video/mp4': $attach[] = '<video style="height: 100px;" controls src="data:'.$type.';base64,'.$base64.'">'; break; case 'application/pdf': $attach[] = '<iframe style="height: 100px;" src="data:'.$type.';base64,'.$base64.'"></iframe>'; break; default: $attach[] = ''; break; } } } ---- フォーム送信イベントも使わないので、ここも不要です。 ---- /** * [event] フォーム送信イベント発生時 */ document.querySelector("#upload").addEventListener("submit", (e) => { // 規定の送信イベントをキャンセル e.preventDefault(); ---- あと、validation()が定義されていないので、single-input.phpから、validation_submit()、validation_text()、validation()をコピーしてきてください。 ただ、そのままではエラーになるかも知れないので気を付けてください。
補足
アドバイスありがとうございます、設計がわからず別途質問してみたところ非同期通信は2度必要なようです… ※アドバイス頂いた内容 回答の入力フォームを追加 (<form> 要素) 入力フォーム内の "確認" ボタンが押されたら (submit イベント) → 入力フォームを非表示 (CSS display などを使用) → 画面内に、回答の確認フォームを追加 → 入力フォームから確認したい内容を確認フォームに書き込み (ここで非同期 GET 送信して正常性確認した結果も書くと親切) 確認フォーム内の "戻る" ボタンが押されたら → 入力フォームを表示 → 確認フォームを削除 確認フォーム内の "送信" ボタンが押されたら (submit イベント) → 入力フォーム内容を非同期で POST 送信 (Fetch API などを使用) → 送信成功なら、 →→ 入力フォームと確認フォームを消去 →→ 画面内に、送信済の回答文ブロックを追加 (<section> 要素など) →→ 画面内に、新たな回答の入力フォームを追加 → 送信失敗なら、 →→ 画面内に、失敗した旨のお詫びブロックを追加
- dell_OK
- ベストアンサー率13% (766/5720)
・修正いただいたコードは確認画面でアップロードファイルのバリデーションが行われており、入力画面の扱いをどうすべきか悩んでおります。 質問者さまの考えられていた方法でいいと思いますので、私のコードは確認画面からの送信と言うことでいいと思います。 非同期送信が1回で済むのでその方がいいと思います。 ・入力されたフォームデータを fetch を使い確認画面に送り、確認画面のデータを fetch を使いデータベースに送るような形になるのではないかと懸念していたのですが、入力フォームはボタン送信時に非表示にするだけで良いのでしょうか? 入力画面は非表示にするだけにして、非同期送信にもそのデータを使う、でいいと思います。 確認画面までフォームにして、それを送信するのは面倒なだけで無駄だと思います。 確認画面はあくまで入力内容を未確認のまま送信させないための一時停止画面と言うことでいいと思います。
補足
Q.入力画面は非表示にするだけにして、非同期送信にもそのデータを使う、でいいと思います。 確認画面までフォームにして、それを送信するのは面倒なだけで無駄だと思います。 確認画面はあくまで入力内容を未確認のまま送信させないための一時停止画面と言うことでいいと思います。 A.アドバイスありがとうございます、非同期送信は確認画面から行うということで考えてみます。入力画面から確認画面にセッションを使いデータを引き継いでサーベー(データベース)に送る際に非同期通信を行うということになりそうですね。 確認画面の下記コードは入力画面の拡張子チェックや MymeType のチェックとは関係ないコードになるのでしょうか? 役割が被っている部分があるようで、どう then/catch を書くべきかアドバイスお願い致します… .then(json => { document.getElementById('result_namae').innerHTML = json.namae; document.getElementById('result_message').innerHTML = json.message; document.getElementById('result_stamp').value = json.stamp; const viewer = document.getElementById('result_viewer'); for (const attach of json.attach) { var child = null; if (attach.type == 'image/jpeg' || attach.type == 'image/png') { child = document.createElement("img"); } else if (attach.type == 'video/mp4') { child = document.createElement("video"); child.setAttribute("controls", null); } else if (attach.type == 'application/pdf') { child = document.createElement("iframe"); } if (child !== null) { child.style.height = "200px"; child.style.width = "200px"; child.src = "data:" + attach.type + ";base64," + attach.data; viewer.appendChild(child); } } document.forms['community_form'].style.display = 'none'; document.getElementById('result').style.display = 'block'; }) then/catch(error => {}); }); ※single-input.phpファイルアップロード reader.onload = () => { var child = null; if (reader.result.indexOf("data:image/jpeg;base64,") === 0 || reader.result.indexOf("data:image/png;base64,") === 0) { child = document.createElement("img"); } else if (reader.result.indexOf("data:video/mp4;base64,") === 0) { child = document.createElement("video"); child.setAttribute("controls", null); } else if (reader.result.indexOf("data:application/pdf;base64,") === 0) { child = document.createElement("iframe"); } else { alert("対象外のファイルです"); alert(reader.result); attach[i].value = ""; } if (child !== null) { child.style.height = "350px"; child.style.width = "528px"; child.src = reader.result; viewer[i].appendChild(child); changeImg[i].classList.add('hideItems'); // もともとの画像を消す }
- dell_OK
- ベストアンサー率13% (766/5720)
・自分の認識では入力画面でバリデーションを行い、確認画面で表示の切り替えのみ行うように考えておりました。 なるほど。 それでしたら、この段階ではまだFetchの出番ではないですね。 コードにfetchが残っていたままだったのでそのまま継続していました。 ・入力画面で非同期通信を行い確認画面を表示させた後にサーバー(データベース)に送信する仕組みになるのではないかと思っておりました。 入力画面ではJavaScriptで確認画面への切り替えのみ。 確認画面から非同期通信の流れになると思います。
補足
A.回答ありがとうございます、入力画面のアップロードファイルの拡張子チェックや MymeType のチェックはどうすれば良いでしょうか?
- dell_OK
- ベストアンサー率13% (766/5720)
表示する方はこんな感じになります。 変更箇所としては。 1.<div id="result" style="display: none;"> で確認画面の初期非表示。 2.<div id="result_viewer"></div> でアップロードファイル表示用の領域を追加。 3.<button type="button" id="result_back_button">戻る</button> で戻るボタンを設置。 4.これは私の都合ですが、質問者さまのサイトURLを直接書かれるとコードを提供された際に私の環境に直さないといけないため、fetchのURLをhome_url()にしました。 5.const viewer から for の終了 } で、アップロードファイルの表示用。 6.回答フォームと確認画面の切り替え。 7.戻るボタンの処理。 ----single-complete.php <div id="result" style="display: none;"> <p id="result_namae"></p> <p id="result_message"></p> <input type="radio" id="result_stamp"><label for="result_stamp"></label> <div id="result_viewer"></div> <button type="button" id="result_back_button">戻る</button> </div> <script> document.getElementById("submit_button").addEventListener('click', (e) => { const formData = new FormData(community_form); // フォームデータをオブジェクトに収容する formData.append('action', 'enquiry_sample'); // サーバーのアクション名 const opt = { method: 'post', body: formData //fetch() を使って FormData をサブミットするには、 送信オプションの body に FormData オブジェクトをセット } fetch('<?php echo home_url('/wp-admin/admin-ajax.php'); ?>', opt) // 送信先 Wordpressの場合変更不可能 .then(response => { //成功したら(then) ,responseのデータをコンソールに表示 if (!response.ok) { throw new Error; } return response.json(); }) .then(json => { document.getElementById('result_namae').innerHTML = json.namae; document.getElementById('result_message').innerHTML = json.message; document.getElementById('result_stamp').value = json.stamp; const viewer = document.getElementById('result_viewer'); for (const attach of json.attach) { var child = null; if (attach.type == 'image/jpeg' || attach.type == 'image/png') { child = document.createElement("img"); } else if (attach.type == 'video/mp4') { child = document.createElement("video"); child.setAttribute("controls", null); } else if (attach.type == 'application/pdf') { child = document.createElement("iframe"); } if (child !== null) { child.style.height = "200px"; child.style.width = "200px"; child.src = "data:" + attach.type + ";base64," + attach.data; viewer.appendChild(child); } } document.forms['community_form'].style.display = 'none'; document.getElementById('result').style.display = 'block'; }) .catch(error => {}); }); document.getElementById("result_back_button").addEventListener('click', () => { document.forms['community_form'].style.display = 'block'; document.getElementById('result').style.display = 'none'; document.getElementById('result_viewer').innerHTML = ''; }); </script> ---- 今までのところ、確認画面は表示のみの動作です。 ここまで動作確認してみてください。 次は、確認画面から、その内容を確定する場合に、そのための操作ボタンが必要になると思います。 確定処理としては、再度FormDataで同じものを送信すればいいと思っています。 回答画面から確認画面へはサーバーのアクション名がenquiry_sampleです。 確認画面から確定処理へは何か別の名前にするか、もうひとつパラメータを追加するかになると思います。 実際は、その前に確認画面の時点で問題のある文字や画像の処理は済ませます。 問題があれば、確認画面は表示せずにエラーとして入力画面になんらかの表示が必要になると思います。
お礼
説明が不足しておりました申し訳ありません。dell_ok さんのコードを参考にさせていただき sample.php にコードを書いてみました。 修正いただいたコードは確認画面でアップロードファイルのバリデーションが行われており、入力画面の扱いをどうすべきか悩んでおります。 入力されたフォームデータを fetch を使い確認画面に送り、確認画面のデータを fetch を使いデータベースに送るような形になるのではないかと懸念していたのですが、入力フォームはボタン送信時に非表示にするだけで良いのでしょうか?
補足
A.修正ありがとうございます、確認してみたのですが表示画面でファイルをアップロードできていない為確認が取れていない状態です… 自分の認識では入力画面でバリデーションを行い、確認画面で表示の切り替えのみ行うように考えておりました。 277行目から308行目までに入力画面の HTML を .style.display = 'none'; で非表示にして、確認画面のHTMLを生成する流れになるのではないでしょうか? Fetch API を使うのが難しいのではないかと以前 dell_ok さんにお伝えしたときの意図として、入力画面で非同期通信を行い確認画面を表示させた後にサーバー(データベース)に送信する仕組みになるのではないかと思っておりました。 参考サイトでは入力画面から送信完了画面に飛んでいるため2画面構成ですが、作成中の掲示板では入力画面から確認画面に切り替わり確認画面から送信するため3画面構成になります。確認画面から送信の際にリダイレクトを挟み別ファイルに表示コードを書くことも考えているのですが、コメントを真下に表示させたい為どのような作りになるのか想像が出来ておりません… ※最新コード https://wandbox.org/permlink/QAgfB9R8vMpK7huG ※参考サイト https://imuza.com/enquiry-form/
- dell_OK
- ベストアンサー率13% (766/5720)
ファイルのアップロードについてはすでに FormData がしてくれているのでなにもしなくて大丈夫です。 サーバー側で受け取ったファイルをどうしていくかと言うのは検討事項として残りますが、とりあえず、アップロードされた画像を確認画面に表示してみましょう。 FormDataはその名の通りフォームのデータで、それを post で送信しています。 通常のフォームを post で submit したのと同じと考えてください。 なので、enquiry_sample()では、<input name="~">のデータを $_POST で使えているわけです。 同じように、アップロードファイルは $_FILES で使えます。 ここではとりあえずですがbase64形式にしてJSONデータとして返すことにします。 ----functions.php function enquiry_sample() { header('Content-type: application/json; charset=UTF-8'); $result = []; $result['namae'] = $_POST['namae']; $result['message'] = $_POST['message']; $result['stamp'] = $_POST['stamp']; foreach ($_FILES['attach']['tmp_name'] as $i => $tmp_name) { if (empty($tmp_name)) { $result['attach'][$i]['data'] = ''; $result['attach'][$i]['type'] = ''; } else { $data = file_get_contents($tmp_name); $result['attach'][$i]['data'] = base64_encode($data); $result['attach'][$i]['type'] = $_FILES['attach']['type'][$i]; } } echo json_encode($result); exit; } add_action('wp_ajax_enquiry_sample', 'enquiry_sample'); add_action('wp_ajax_nopriv_enquiry_sample', 'enquiry_sample'); ---- つづく。
- dell_OK
- ベストアンサー率13% (766/5720)
・アップロードファイルのMIMEタイプを調べたり、ファイル形式を調べたりする機能をsingle-input.phpで書いていると思うのですが、参考サイトのものを使う方が適切でしょうか? どうでしょうね。 single-input.phpのものがどこまでそのまま使えるかはやってみないとわかりませんが、似ている処理とは言え違う作者のコードを混ぜるよりは、single-input.phpのものを使った方が無難な気がします。 single-input.phpにはプレビュー機能があるので参考サイトのものだけでは不足しますし。 ・確認ページに戻るボタンを設置する方法をどのように作成すべきか悩んでおります。 ボタンは <div id="result">~~~</div> の中に配置すればいいと思います。 クリックされた時には、このdivを非表示にして、community_formを表示するだけでいいと思います。
補足
アドバイスありがとうございます、sample.phpにアップロードファイルについて考えたコードを書いてみたのですが、105行目からのアップロードの成否について悩んでおります。表示を切り替える場合この部分にはどのようなコードが必要になるのでしょうか? 125行目からのサーバーへ保存するコードはデータベースに変更するように考えております。 ※現在のコード https://wandbox.org/permlink/fEmapDkUlyvIONhE ※参考サイト https://blog.katsubemakito.net/html5/fetch2 https://imuza.com/enquiry-form/
- dell_OK
- ベストアンサー率13% (766/5720)
※最新コードのsingle-complete.phpでコードはほぼよさそうです。 ここは、 ---- const formData = new URLSearchParams(community_form); // フォームデータをオブジェクトに収容する ---- これにしてみてください。 ---- const formData = new FormData(community_form); // フォームデータをオブジェクトに収容する ---- ここは、 ---- fetch('<?php echo home_url('https://www.irasuto.cfbx.jp/wp-admin/admin-ajax.php'); ?>', opt) // 送信先 Wordpressの場合変更不可能 ---- これにしてみてください。 ---- fetch('https://www.irasuto.cfbx.jp/wp-admin/admin-ajax.php', opt) // 送信先 Wordpressの場合変更不可能 ----
補足
アドバイスありがとうございます、表示することが出来ました。 アップロードファイルのMIMEタイプを調べたり、ファイル形式を調べたりする機能をsingle-input.phpで書いていると思うのですが、参考サイトのものを使う方が適切でしょうか? 上記とは別の機能になるのですが確認ページに戻るボタンを設置する方法をどのように作成すべきか悩んでおります。 ※最新コード https://wandbox.org/permlink/7ajSEzvb9eq7FTnk ※参考サイト https://blog.katsubemakito.net/html5/fetch2
補足
Q.ローカルストレージなどに保存しないと入力内容が消えるのではないかというアドバイス 必ず消えるとかでないのなら、消えた場合に入力画面に戻るようにして、再入力してもらえばいいと思います。 消えないと言うか消えてもいいと言うか、セッションならこれは問題になりません。 確認画面から結果画面へ行く時は、「確認画面から結果画面へ行くよ」と言う情報しか(改ざんされたデータがない)サーバーに送信しないので安全です。 A.回答ありがとうございます、dell_ok さんと同じように考えている方もおられるようで消えないかもしれません… 条件で消えるようであれば再入力してもらう方法がよさそうですね。 非同期通信という事で画面が還移する場合と違うのではないかと心配になっておりました。データをチェックして対策すれば問題ということでコードを考えていきます。