application.properties
にspring.servlet.multipart.max-file-size=100
のように設定することでサイズ制限設定可能。- Spring Frameworkのファイルアップロード機能は、Webコンテナが持っているmultipartリクエストの処理機能をそのまま使う*1。ファイルサイズ上限のチェックに引っかかったときのスタックトレースを見ると、Tomcatでは
org.apache.tomcat.util.http.fileupload
パッケージが使われている。これにはcommons-fileuploadからコピーされてきたコードが使われている*2。 - Webコンテナのmultipartリクエストのサイズ制限機能は、
web.xml
からも設定できる*3。プログラム内でも指定はできるが、指定をするためのMultipartConfigElement
はSpringアプリケーション1つに対して1個のようなので*4、Controllerごと・URLごとのファイルサイズ制限は行えない模様*5。 - URLごとのファイルサイズ制限を指定するアノテーションをググると
@MultipartConfig
がある。これはJavaEE仕様(javax.servlet.annotation.MultipartConfig
)なので、少なくともSpringだけでは利用できない模様*6。
- Spring Frameworkのファイルアップロード機能は、Webコンテナが持っているmultipartリクエストの処理機能をそのまま使う*1。ファイルサイズ上限のチェックに引っかかったときのスタックトレースを見ると、Tomcatでは
max-file-size
によって発生したエラーは、Controllerではハンドリングできない。URLごとに個別のファイルサイズ制限をしたい、またはファイルサイズ上限エラーのハンドリングをビジネスロジックの中で行い、ビジネスロジックのエラーとしてレスポンスに含めたいという場合は、MultipartFile#getSize()
を呼び出して自分で判定を行う必要がある*7。例外としてはSpring Frameworkがorg.springframework.web.multipart.MaxUploadSizeExceededException
を提供しているのでこれを使えばよい。application.properties
のfile-size-threshold
がデフォルトなら、アップロードされたファイルは常に一時ファイルへ格納されJavaヒープ上には格納されないので、大きいファイルをアップロードされてJavaのヒープが溢れるといったことはなさそう。- 一時ファイルはリクエストが処理されたらすぐに削除されるので、ハウスキープ処理は基本的に必要なさそう。
- 一時ファイルが作られる先は、OSのテンポラリディレクトリの下の
tomcat.【ポート番号】.【Tomcatを立ち上げるたびに決まる乱数?】\work\Tomcat\【ホスト名】\
以下。ファイル削除が必要な場合はそこを削除する。
01: POST /upload HTTP/1.1 02: HOST: localhost:8080 03: content-type: multipart/form-data; boundary=----WebKitFormBoundaryqx0dK5IFp8lW2Hjr 04: content-length: 500 05: 06: ------WebKitFormBoundaryqx0dK5IFp8lW2Hjr 07: Content-Disposition: form-data; name="hoge"; filename="blob" 08: Content-Type: text/plain 09: 10: piyo 11: ------WebKitFormBoundaryqx0dK5IFp8lW2Hjr 12: Content-Disposition: form-data; name="file"; filename="100.txt" 13: Content-Type: text/plain 14: Content-Length: 150 15: 16: 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890............................................................ 17: ------WebKitFormBoundaryqx0dK5IFp8lW2Hjr--
- 上記のようなリクエストがあった場合、以下のように処理が行われる。
- 4行目の
Content-Length
は必須。これがなかった場合、multipartリクエストとして扱われない(ファイル部分は無視されてしまう)。 - 4行目の
Content-Length
からファイル部分のサイズを逆算するなどはしない。 - 14行目の
Content-Length
はあってもなくてもよい。ChromeのAdvanced REST Clientの場合、14行目のContent-Length
は付与されない。 - 14行目の
Content-Length
がある場合、これを読み込んだ時点でmax-file-size
との比較が行われる。パスしなかった場合は即エラーとなり、以降のデータは読み込まれない。 - 14行目の
Content-Length
がない場合は、16行目以降の内容を読み込み始める。max-file-size
を超える内容を読み込んだ時点で即エラーとなる。エラーとなった場合、以降のデータは読み込まれない。 - 14行目の
Content-Length
があって、かつmax-file-size
よりも小さい場合は、Content-Length
がない場合と同じ処理になる。 - 上記で「即エラーとなる」というのは、101バイト目を読み込んだ時点でエラーとなるわけではなく、もう少し(数十バイト?)読み込んでからエラーが返る。
- 4行目の
*1:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-multipart-resolver-standard
*2:https://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/tomcat/util/http/fileupload/package-summary.html
*3:https://stackoverflow.com/a/19145563/3902663
*4:https://docs.spring.io/spring-framework/docs/5.3.10/javadoc-api/org/springframework/web/multipart/support/StandardServletMultipartResolver.html