西森貫太
バックエンド
フロントエンド
form-dataで日本語ファイルがエラーとなる
なぜ日本語ファイル名で問題が起きるのか?
postmanを使って画像ファイルを扱う際に、エラーに遭遇したのでファイルを扱う際の注意事項の1つとしてまとめました。
multipart/form-data で送信されるファイルには、以下のようなヘッダーが付きます:
css
コピーする
Content-Disposition: form-data; name="file"; filename="日本語ファイル.png"
Content-Type: ...
HTTP の仕様上、 filename の部分は ASCII 文字のみを想定しており、日本語などのマルチバイト文字の使用は推奨されていません。
※ RFCで部分的に追加規定されているor今後される可能性はあります。
そのため「filename*="utf-8''xxx" 」といったエンコード形式の使用が推奨されているが、開発環境(Nodeであればバージョンの違いなど)によって対応状況が異なります。
今回の件は、「Node.js 18 まではうまくパースしていたけれど、Node.js 22 でうまくいかなくなった」
という状況であり、 Node の内部や fetch / FormData 実装が変わり、マルチバイト対応の差異が顕在化した可能性が高いです。
対処策
クライアント側でファイル名を ASCII(英数字)に置き換える(最もシンプルかつ確実)
- 日本語ファイル名を入力させる場合でも、実際にアップロードする時点でリネームしてしまう
RFCに準拠したファイル名送信
filename*="utf-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.png"のように、UTF-8 URLエンコードしたファイル名をfilename*で送る。
マルチパートを自前でパース(あるいはライブラリを使う)
busboyやmulterのようなライブラリを使うと、独自の実装でFormDataの厳格なエンコードルールに依存せず、より柔軟にmultipart/form-dataを解析できます。(※ ただし実装が複雑化し難易度は高くなります)
まとめ
日本語ファイル名のせいでエラーになっていた
multipart/form-dataで送信されるデータのヘッダー内の文字コードの制限によるもの- Node.js (もしくは Next.js) 内部実装の差異が原因。
対処策
- アップロード前にファイル名をリネームして ASCII 文字にする( ※ サーバーサイドで
uuidを使って保存名を変えるなら実質問題なし) - どうしてもクライアントで日本語のオリジナルファイル名を保持してアップロードしたい場合は、RFCに基づいた送信が必要だったり、
filenameのマルチバイトエンコードを正しく扱うライブラリが必要になります。
