かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

React axios でファイルアップロードしたい

フロントを React バックエンドを PHP で作成していて、ファイルのアップロードを試してみたのでメモ

File を扱うコンポーネント

React では、<input type="file" /> は値がユーザだけが設定できるものでありプログラムでは操作できないため、常に非制御コンポーネントです。
cf. 非制御コンポーネント – React

input[type="file"]value はプログラムで設定できないので値は FileAPI を使って取得する必要があるトノコト。

input[type="file"]value は設定できないけど、onChange で選択されたファイルについては files[0] で取得できる Fileオブジェクト を state として持つことはできるっぽい

function InputFile() {
  const [file, setFile] = useState(null);
  
  const onChangeHandler = useCallback((e) => {
    // ファイル選択がキャンセルされた時は undefined
    const file = e.target.files[0];
    setFile(() => {
      return file ? file : null;
    });
  }, []);

  return <input type="file" accept="image/*" onChange={onChangeHandler} />
}

state でファイルオブジェクトを持ってしまうので、公式リファレンスにあるように useRef を使って必要なときだけ ファイルオブジェクトを取り出すようにした方がメモリ効率は良いのかも知れない。

File を含むデータを axios で送信する

以前書いた記事 JavaScript (SPA) PHP axios でフォームデータを送る時に気をつけること - かもメモ でバックエンドの PHP$_POST でフォームデータを取得できるようにするには URLSearchParams 形式で送れば OK と書いていたのですが、URLSearchParams で送った場合 $_FILESnull になってしまいうまく取得できませんでした。

ファイルを含むフォームデータは FormData 形式の Content-Type: 'multipart/form-data' で送るのが良いみたいです。

フロント

function PostForm() {
  const ref = useRef(null);

  const onPostForm = useCallback(async(data) => {
    try {
      // Object の data を FormData 形式に変換する
      const params = new FormData();
      Object.keys(data).forEach(function(key) {
        params.append(key, this[key]);
      }, data);
      const res = await axios.post(API_URL, params, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });
      console.log(res);
    } catch(err) {
      console.log(err);
    }
  }, []);
  
  const onSubmitHandler = useCallback((e) => {
    e.preventDefault();
    onPostForm({
      file: ref.current.files[0],
    });
  }, [onPostForm]);

  return (
    <form onSubmit={onSubmitHandler}>
      <input type="file" accept="image/*" ref={ref} />
      {/* 略 */}
    </form>
  );
}

バックエンド (PHP)

<?php
$file = $_FILES['file'];

header('Content-Type: application/json; charset=utf-8', true, 200);
echo json_encode([
  'file_name' => $file['name'],
  'tmp_file' => $file['tmp_name'],
  'error' => $file['error'],
], JSON_UNESCAPED_UNICODE);

FormData 形式・Content-Type: 'multipart/form-data' で送信すれば PHP$_FILES で送られたファイルデータを取得することができました!
₍ ᐢ. ̫ .ᐢ ₎ ヤッタネ!!


[参考]