🎆

ファイルを渡すinputタグの入力情報を画面遷移後も保持しておくには

2024/02/04に公開

HTMLのフォームを送信してバリデーションチェックがエラーの場合、Webのシステムでは

  1. 通常のリクエスト送信
  2. サーバー側でチェック(エラー)
  3. レスポンス返信
  4. 入力値復元

の流れになることがよくあります。

4はフォーム入力情報をプログラムで設定すると思うのですが、type属性が"file"のinputタグに対してはプログラムからの変更ができないという場面に業務で遭遇しました。
ちなみに、変更できるということはファイルを改竄できるということになるので、セキュリティ上の観点からそうなっているようです。

上記1~4のフローでは復元できなかったですが、なんとかする方法を考えてみました。

案1:非同期リクエストでバリデーションを行う

先にAjaxで入力チェックのためのフォーム送信をやっておいて、結果に問題がなければ同期リクエストでフォームを送信します。
つまり1~3のフローを実装します。

  1. 先にAjaxリクエストでフォームを送信し、非同期リクエストでバリデーションを行う
  2. バリデーションの結果をレスポンスとして返却
  3. 2の結果が、
     ・OK:同期リクエストでフォーム送信
     ・NG:フォーム送信せずエラー時の処理をする(例:エラーメッセージを表示する)

こうすることで画面遷移が起こらず実質的に入力値復元が実現できます(復元というより保持の方が意味合いが近いですが...)。
業務ではこの案で実装して、無事にプルリクエスト承認されました。

実際のソースは秘密保持の観点から載せられないので、サンプルコードを書いてみました。
HTML部分(フォームの例):

<!DOCTYPE html>
<html>
<head>
    <title>File Upload Validation Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
    <form id="uploadForm" method="post"  enctype="multipart/form-data">
        <input type="file" id="fileInput" name="file" />
        <input type="button" id="submitButton" value="ファイルアップロード" />
    </form>
    <div id="message"></div>

    <script src="script.js"></script>
</body>
</html>

JavaScript部分(Ajaxによる非同期リクエストと結果に応じたアクション):

document.getElementById('submitButton').addEventListener('click', function(e) {
    const formData = new FormData();
    const fileInput = document.getElementById('fileInput');
    const messageDiv = document.getElementById('message');

    if (fileInput.files.length > 0) {
        formData.append('file', fileInput.files[0]);

        // Ajaxリクエストでファイルをサーバーに送信しバリデーションチェックを行う
        $.ajax({
            url: 'validateFile.php', // ファイルバリデーションのためのサーバーサイドスクリプトのURL
            type: 'POST',
            data: formData,
            contentType: false,
            processData: false,
            success: function(response) {
                // レスポンスに基づいて処理を分岐
                if (response.valid) {
                    // バリデーションOKの場合、同期リクエストでフォーム送信を行う
                    // 実際にはここでフォームを送信するコードを記述します
                    messageDiv.innerHTML = 'ファイルは有効です。フォームの送信を続行します...';
                } else {
                    // バリデーションNGの場合、エラーメッセージを表示
                    messageDiv.innerHTML = 'バリデーション失敗: ' + response.message;
                }
            },
            error: function() {
                messageDiv.innerHTML = 'ファイルの検証中にエラーが発生しました。';
            }
        });
    } else {
        messageDiv.innerHTML = 'アップロードするファイルを選択してください。';
    }
});

この例では、validateFile.phpはサーバーサイドでファイルのバリデーションを行うスクリプトを指します。実装はこの例には含まれていませんがファイルを検証し、バリデーションの結果をJSON形式で返すことを想定しています。返されるJSONオブジェクトには、バリデーションが成功したかどうかを示すvalidキーと、失敗した場合のエラーメッセージを含むmessageフィールドが含まれる前提としています。

案2:入力情報がなくなっていることを通知する

当初のフローは変えずに、ユーザーに入力したファイルがなくなっていることを通知します。つまり復元をあきらめるということですが

script.js
alert("ファイルを再選択してください。")

とかを追加するだけでよければ案1より実装コストは低くなる可能性が高いなどメリットはあります。開発プロジェクトの関係者と話し合った結果これでもよいこともあり得るのではないでしょうか。
この案は"そもそもどうしても保持しなければいけないのか"について着目して考えた結果です。こんな感じで、"なぜそうするのか"や"そもそもどうしても必要な要素なのか"のようなところにさかのぼって考えたり関係者とコミュニケーションをとってみるのは詰まってしまったときに有効かもしれません。

参考にした記事

https://atmarkit.itmedia.co.jp/bbs/phpBB/viewtopic.php?topic=1854&forum=7
https://stackoverflow.com/questions/1696877/how-to-set-a-value-to-a-file-input-in-html/1696884#1696884

Discussion