natural born minority
本記事は この記事 の翻訳&実証みたいなものなのですが…。
タイトルの通り 「Springの MultipartFile.getInputStream() は何回呼んでも中身が取り出せるのだ」 ということを確認しました。
と、言うのも
「Javaの ServletRequest#getInputStream
メソッドは、複数回読むことができない(実行すると二回目が IllegalStateException
を吐く)」
というよく知られた挙動があり、以前に「無理やり複数回呼べるように対応した」経緯から、 「 MultipartFile#getInputStream
もどーせ複数回読めないんでしょ?」と思い込んでいたのです。
が、先の記事と「実際、読めたのを実証した」ので記録しときます。
Gradle Project', Group:
com.kazuhitom` とし、それ以外はデフォルトSpring Web
を加えるdemo.zip
を展開展開したZipのフォルダに、 package
の階層だけあわせて、以下のクラスを作成します。
uplaod_test.sh
はテスト用のスクリプトなので、プロジェクトの直下など好きなとこに置いてください。
bash
を実行できるコンソールを開き、展開したZipフォルダまで移動、gradle
にてSpringBootのWebアプリケーションを起動してください。
./gradlew bootRun
また別の bash
を実行できるコンソールを開き、前述の upload_test.sh
を実行してください。
chmod u+x ./upload_test.sh
./upload_test.sh
すると、SpringBootを起動した側のコンソールに、
と出力され「 multipartFile#getInputStream
が複数回呼び出されて、かつファイルの内容で読み出せてる」ことがわかります。
最初に 以前に「無理やり複数回呼べるように対応した」
と書きましたが、そのソース内でチームメンバーが「 multipartFile#getInputStream
を二回呼んでいる」というコードを書いたので、それをレビューしたのですが…
「当人は動くと言っている…がそれが”以前の無理やり対応”によるものか、元々の仕様としてそう振る舞うのか」
が解らず、指摘すべきかを迷ったのです。
指摘するにも「実際に検証しないと指摘にもならない」ので、調べ始めた…のがこの記事の発端です。
「思い込みによるイチャモン」にならず、実際動かすことにより確証が得られたので良かったです。
ただ、これは「Streamとは別にメモリ中(あるいは一時ファイル)にデータを持っている」ということを意味するのかも?
ということは
readAllBytes()
とかしちゃうと、最低でも「実ファイル * 2の容量」のメモリを食うかもしれませんね…。
ServletRequest#getInputStream
の振る舞いについて、二回以上呼ぶためには…の議論