ファイルアップロード
================================================================================
.. only:: html
.. contents:: 目次
:depth: 3
:local:
.. _FileUploadOverview:
Overview
--------------------------------------------------------------------------------
| 本節では、ファイルをアップロードする方法について、説明する。
| ファイルのアップロードは、Servlet 3.0からサポートされたファイルアップロード機能と、Spring Webから提供されているクラスを利用して行う。
|
アップロード処理の基本フロー
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Servlet 3.0からサポートされたファイルアップロード機能と、Spring Webのクラスを使って、ファイルをアップロードする際の基本フローを、以下に示す。
.. figure:: ./images/file-upload-overview_basicflow.png
:alt: Screen image of single file upload.
:width: 100%
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
:class: longtable
* - 項番
- 説明
* - | (1)
- | アップロードするファイルを選択し、アップロードを実行する。
* - | (2)
- | サーブレットコンテナは、\ ``multipart/form-data``\ リクエストを受け取り、\ ``org.springframework.web.multipart.support.MultipartFilter``\ を呼び出す。
* - | (3)
- | \ ``MultipartFilter``\ は、 \ ``org.springframework.web.multipart.support.StandardServletMultipartResolver``\ のメソッドを呼び出し、Servlet 3.0のファイルアップロード機能を、Spring MVCで扱えるようにする。
| \ ``StandardServletMultipartResolver``\ は、Servlet 3.0から導入されたAPI( \ ``javax.servlet.http.Part``\ )をラップする \ ``org.springframework.web.multipart.MultipartFile``\ のオブジェクトを生成する。
* - | (4)
- | \ ``MultipartFilter``\ から \ ``DispatcherServlet``\ にフィルタチェーンする。
* - | (5)
- | \ ``DispatcherServlet``\ は、Controllerのハンドラメソッドを呼び出す。
| (3)で生成された \ ``MultipartFile``\ オブジェクトは、 Controllerの引数またはフォームオブジェクトに、バインドされる。
* - | (6)
- | Controllerは、 \ ``MultipartFile``\ オブジェクトのメソッドを呼び出し、アップロードされたファイルの中身と、メタ情報(ファイル名など)を取得する。
* - | (7)
- | \ ``MultipartFile``\ は、Servlet 3.0から導入された \ ``Part``\ オブジェクトのメソッドを呼び出し、アップロードされたファイルの中身と、メタ情報(ファイル名など)を取得し、Controllerに返却する。
* - | (8)
- | Controllerは、Serviceのメソッドを呼び出し、アップロード処理を実行する。
| \ ``MultipartFile``\ オブジェクトより取得した、ファイルの中身と、メタ情報(ファイル名など)は、Serviceのメソッドの引数として、引き渡す。
* - | (9)
- | Serviceは、アップロードされたファイルの中身と、メタ情報(ファイル名など)を、ファイルまたはデータベースに格納する。
* - | (10)
- | \ ``MultipartFilter``\ は、 \ ``StandardServletMultipartResolver``\ を呼び出し、Servlet 3.0のファイルアップロード機能で使用される一時ファイルを削除する。
* - | (11)
- | \ ``StandardServletMultipartResolver``\ は、Servlet 3.0から導入された \ ``Part``\ オブジェクトのメソッドを呼び出し、ディスクに保存されている一時ファイルを削除する。
.. raw:: latex
\newpage
.. note::
Controllerでは、Spring Webから提供されている\ ``MultipartFile``\ オブジェクトに対して処理を行うため、Servlet 3.0から提供されたファイルアップロード用のAPIに依存した実装を、排除することができる。
Spring Webから提供されているクラスについて
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Spring Webから提供されているファイルアップロード用のクラスについて、説明する。
.. tabularcolumns:: |p{0.10\linewidth}|p{0.40\linewidth}|p{0.50\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 40 50
* - | 項番
- | クラス名
- | 説明
* - 1.
- | org.springframework.web.multipart.
| MultipartFile
- | アップロードされたファイルであることを示すインタフェース。
| 利用するファイルアップロード機能で扱うファイルオブジェクトを、抽象化する役割をもつ。
* - 2.
- | org.springframework.web.multipart.support.
| StandardMultipartHttpServletRequest$
| StandardMultipartFile
- | Servlet 3.0から導入されたファイルアップロード機能用の\ ``MultipartFile``\ クラス。
| Servlet 3.0から導入された\ ``Part``\ オブジェクトに、処理を委譲している。
* - 3.
- | org.springframework.web.multipart.
| MultipartResolver
- | \ ``multipart/form-data``\ リクエストの解析方法を解決するためのインタフェース。
| ファイルアップロード機能の、実装に対応する\ ``MultipartFile``\ オブジェクトを生成する役割をもつ。
* - 4.
- | org.springframework.web.multipart.support.
| StandardServletMultipartResolver
- | Servlet 3.0から導入されたファイルアップロード機能用の\ ``MultipartResolver``\ クラス。
* - 5.
- | org.springframework.web.multipart.support.
| MultipartFilter
- | multipart/form-dataリクエストの時に、DIコンテナからMultipartResolverを実装するクラスを呼び出し、MultipartFileを生成するクラス。
| このクラスを使用しないと、ファイルアップロードで許容する最大サイズを超えた場合に、Servlet Filterの処理内でリクエストパラメータを取得できない。
| そのため、本ガイドラインではMultipartFilterを使用することを推奨している。
|
How to use
--------------------------------------------------------------------------------
.. _file-upload_how_to_usr_application_settings:
アプリケーションの設定
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _file-upload_how_to_enable_Servlet_3.0:
Servlet 3.0のアップロード機能を有効化するための設定
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Servlet 3.0のアップロード機能を有効化するために、以下の設定を行う。
- :file:`web.xml`
.. code-block:: xml
:emphasize-lines: 11-15
org.springframework.web.servlet.DispatcherServlet
5242880
27262976
0
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
:class: longtable
* - 項番
- 説明
* - | (1)
- | \ ````\ 要素の\ ``xsi:schemaLocation``\ 属性に、Servlet 3.0以上のXSDファイルを指定する。
* - | (2)
- | \ ````\ 要素の\ ``version``\ 属性に、\ ``3.0``\ 以上のバージョンを指定する。
* - | (3)
- | ファイルアップロードを使用するServletの\ ````\ 要素に、\ ````\ 要素を追加する。
* - | (4)
- | アップロードを許可する1ファイルの最大バイト数を指定する。
| 指定がない場合、-1 (制限なし)が設定される。
| 指定した値を超えた場合、\ ``org.springframework.web.multipart.MultipartException``\ が発生する。
|
| 上記例では、 5MBを指定している。
* - | (5)
- | \ ``multipart/form-data``\ リクエストのContent-Lengthの最大値を指定する。
| 指定がない場合、-1 (制限なし)が設定される。
| 指定した値を超えた場合、\ ``org.springframework.web.multipart.MultipartException``\ が発生する。
|
| 本パラメータに設定する値は、以下の計算式で算出される値を設定する必要がある。
|
| **(「アップロードを許可する1ファイルの最大バイト数」 * 「同時にアップロードを許可するファイル数」 ) + 「その他のフォーム項目のデータサイズ」 + 「multipart/form-dataリクエストのメタ情報サイズ」**
|
| 上記例では、 26MBを指定している。
| 内訳は、25MB(5MB * 5 files)と、1MB(メタ情報のバイト数 + フォーム項目のバイト数)である。
* - | (6)
- | アップロードされたファイルの中身を、一時ファイルとして保存するかの閾値(1ファイルのバイト数)を指定する。
| このパラメータを明示的に指定しないと ```` 要素や ```` 要素で指定した値が有効にならないアプリケーションサーバが存在するため、デフォルト値(0)を明示的に指定している。
.. raw:: latex
\newpage
.. warning::
DoS攻撃に対する攻撃耐性を高めるため、\ ``max-file-size``\ と、\ ``max-request-size``\ は、かならず指定すること。
DoS攻撃については、\ :ref:`file-upload_security_related_warning_points_dos`\ を参照されたい。
.. note::
Spring Framework 5.0より、指定サイズを超えるファイルのアップロードやマルチパートのリクエストが行われた際、Tomcatなど一部のアプリケーションサーバ上では\ ``org.springframework.web.multipart.MultipartException``\ のサブクラスである\ ``org.springframework.web.multipart.MaxUploadSizeExceededException``\ が発生するようになった。
.. note::
デフォルトの設定では、アップロードされたファイルは必ず一時ファイルに出力されるが、\ ````\ の子要素である\ ````\ 要素の設定値によって、出力有無を制御することができる。
.. code-block:: xml
32768
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (7)
- | アップロードされたファイルの中身を、一時ファイルとして保存するかの閾値(1ファイルのバイト数)を指定する。
| 指定がない場合、0が設定される。
| 指定値を超えるサイズのファイルがアップロードされた場合、アップロードされたファイルは、
| 一時ファイルとしてディスクに出力され、リクエストが完了した時点で削除される。
|
| 上記例では、 32KBを指定している。
.. warning::
本パラメータは、以下の点でトレードオフの関係となっているため、\ **システム特性にあった設定値を指定すること。**\
* 設定値を大きくすると、メモリ内で処理が完結するため、処理性能は向上するが、 DoS攻撃などによって\ ``OutOfMemoryError``\ が発生する可能性が高くなる。
* 設定値を小さくすると、メモリの使用率を最小限に抑えることができるため、DoS攻撃などによって\ ``OutOfMemoryError``\ が発生する可能性を抑えることができるが、
ディスクIOの発生頻度が高くなるため、性能劣化が発生する可能性が高くなる。
一時ファイルの出力ディレクトリを変更したい場合は、\ ````\ の子要素である\ ````\ 要素にディレクトリパスを指定する。
.. code-block:: xml
/tmp
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (8)
- | 一時ファイルを出力するディレクトリのパスを指定する。
| 省略した場合、アプリケーションサーバの一時ファイルを格納するためのディレクトリに出力される。
|
| 上記例では、\ ``/tmp``\ を指定している。
.. warning::
\ ````\ 要素で指定するディレクトリは、アプリケーションサーバ(サーブレットコンテナ)が利用するディレクトリであり、**アプリケーションからアクセスする場所ではない。**
アプリケーションとしてアップロードされたファイルを一時的なファイルとして保存しておきたい場合は、\ ````\ 要素で指定するディレクトリとは、別のディレクトリに出力すること。
.. _file-upload_setting_servlet_filter:
Servlet Filterの設定
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
multipart/form-dataリクエストの時、ファイルアップロードで許容する最大サイズを超えた場合の動作は、アプリケーションサーバによって異なる。アプリケーションサーバによっては、許容サイズを超えたアップロードの際に発生する\ ``MultipartException``\ が検知されず、後述する例外ハンドリングの設定が有効にならない場合がある。
| この動作は\ ``MiltipartFilter``\ を設定することで回避できるため、本ガイドラインでは\ ``MiltipartFilter``\ の設定を前提として説明を行う。
| 以下に、設定例を示す。
- :file:`web.xml`
.. code-block:: xml
MultipartFilter
org.springframework.web.multipart.support.MultipartFilter
MultipartFilter
/*
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (1)
- | Servlet Fliterとして \ ``MultipartFilter``\ を定義する。
* - | (2)
- | \ ``MultipartFilter``\ を適用するURLのパターンを指定する。
.. warning:: **Spring Security使用時の注意点**
Spring Securityを使ってセキュリティ対策を行う場合は、\ ``springSecurityFilterChain``\ より前に定義すること。
また、プロジェクト独自で作成するServlet Filterでリクエストパラメータにアクセスするものがある場合は、そのServlet Filterより前に定義すること。
ただし、\ ``springSecurityFilterChain``\ より前に定義することで、認証又は認可されていないユーザーからのアップロード(一時ファイル作成)を許容することになる。
この動作を回避する方法が\ `Spring Security Reference -Include CSRF Token in URL- `_\ の中で紹介されているが、セキュリティ上のリスクを含む回避方法になるため、本ガイドラインでは回避策の適用は推奨していない。
.. warning:: **ファイルアップロードの許容サイズを超過した場合の注意点**
.. note:: **MultipartResolverのデフォルト呼び出し**
\ ``MultipartFilter``\ を使用すると、デフォルトで
\ ``org.springframework.web.multipart.support.StandardServletMultipartResolver``\ が呼び出される。
\ ``StandardServletMultipartResolver``\ は、アップロードされたファイルを\ ``org.springframework.web.multipart.MultipartFile``\ として生成し、Controllerの引数およびフォームオブジェクトのプロパティとして、受け取ることができるようにする。
例外ハンドリングの設定
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
許可されないサイズのファイルやマルチパートのリクエストが行われた際に発生する\ ``MultipartException``\ の例外ハンドリングの定義を追加する。
| \ ``MultipartException``\ は、クライアントが指定するファイルサイズに起因して発生する例外なので、クライアントエラー(HTTPレスポンスコード=4xx)として扱うことを推奨する。
| **例外ハンドリングを個別に追加しないと、システムエラー扱いとなってしまうので、かならず定義を追加すること。**
| \ ``MultipartException``\ をハンドリングするための設定は、\ ``MultipartFilter``\ を使用するか否かによって異なる。
| \ ``MultipartFilter``\ を使用する場合は、サーブレットコンテナの\ ````\機能を使って例外ハンドリングを行う。
| 以下に、設定例を示す。
- :file:`web.xml`
.. code-block:: xml
org.springframework.web.multipart.MultipartException
/common/error/fileUploadError
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (1)
- | ハンドリング対象の例外クラスとして、\ ``MultipartException``\を指定する。
* - | (2)
- | \ ``MultipartException``\ が発生した際に遷移するパスを指定する。
|
| 上記例では、\ ``/common/error/fileUploadError``\ を指定している。
- :file:`CommonErrorController.java`
.. code-block:: java
@Controller
@RequestMapping("common/error")
public class CommonErrorController {
// omitted
@RequestMapping("fileUploadError")
@ResponseStatus(HttpStatus.BAD_REQUEST) // (3)
public String fileUploadError() {
return "common/error/fileUploadError";
}
}
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (3)
- | HTTPステータスコードは、\ ``@ResponseStatus``\ のアノテーションを付与して設定する。
|
| 上記例では、\ ``400``\ (Bad Request) を設定している。
| 明示的に設定しない場合、HTTPステータスコードは\ ``500``\ (Internal Server Error)となる。
|
| \ ``MultipartFilter``\ を使用しない場合は、\ ``SystemExceptionResolver``\を使用して例外ハンドリングを行う。
| 以下に、設定例を示す。
- :file:`spring-mvc.xml`
.. code-block:: xml
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (4)
- | \ ``SystemExceptionResolver``\ の\ ``exceptionMappings``\ に、\ ``MultipartException``\ が発生した際に表示するView(ThymeleafのテンプレートHTML)の定義を追加する。
|
| 上記例では、\ ``common/error/fileUploadError``\ を指定している。
* - | (5)
- | ``MultipartException`` が発生した際に応答するHTTPステータスコードの定義を追加する。
|
| 上記例では、\ ``400``\ (Bad Request) を指定している。
| クライアントエラー(HTTPレスポンスコード = 4xx)を指定することで、
| 共通ライブラリの例外ハンドリング機能から提供しているクラス( ``HandlerExceptionResolverLoggingInterceptor`` )によって出力されるログは、\ ``ERROR``\ レベルではなく、\ ``WARN``\ レベルとなる。
|
| \ ``MultipartException``\ に対する例外コードを設ける場合は、例外コードの設定を追加する。
| 例外コードは、共通ライブラリのログ出力機能により出力されるログに、出力される。
| 例外コードは、View(テンプレートHTML)から参照することもできる。
| View(テンプレートHTML)から例外コードを参照する方法については、\ :ref:`exception-handling-how-to-use-codingpoint-view-exceptioncode-label`\ を参照されたい。
- :file:`applicationContext.xml`
.. code-block:: xml
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (6)
- | \ ``SimpleMappingExceptionCodeResolver``\ の\ ``exceptionMappings``\ に、\ ``MultipartException``\ が発生した際に適用する、例外コードを追加する。
|
| 上記例では、\ ``e.xx.fw.6001``\ を指定している。
| 個別に定義を行わない場合は、\ ``defaultExceptionCode``\ に指定した例外コードが適用される。
.. note::
Tomcatなど一部のアプリケーションサーバでは、\ ``MaxUploadSizeExceededException``\ をハンドリングすることで、アップロードされたファイルやリクエストのサイズ超過を検知することが可能である。
\ ``MaxUploadSizeExceededException``\ は\ ``MultipartException``\ のサブクラスであるため、サイズ超過とその他の例外を区別する必要がなければ\ ``MultipartException``\ をハンドリングすれば良い。
\ ``MaxUploadSizeExceededException``\ については、:ref:`file-upload_how_to_enable_Servlet_3.0` のNoteを参照されたい。
単一ファイルのアップロード
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
単一ファイルをアップロードする方法について、説明する。
.. figure:: ./images/file-upload-how_to_use_single.png
:alt: Screen image of single file upload.
:width: 100%
| 単一ファイルの場合は、\ ``org.springframework.web.multipart.MultipartFile``\ オブジェクトを、フォームオブジェクトにバインドして受け取る方法と、Controllerの引数として直接受け取る2つの方法があるが、本ガイドラインでは、フォームオブジェクトにバインドして受け取る方法を推奨する。
| その理由は、アップロードされたファイルの単項目チェックを、Bean Validationの仕組みを使って行うことができるためである。
以下に、フォームオブジェクトにバインドして受け取る方法について、説明する。
フォームの実装
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.. code-block:: java
public class FileUploadForm implements Serializable {
// omitted
private MultipartFile file; // (1)
@NotNull
@Size(min = 0, max = 100)
private String description;
// omitted getter/setter methods.
}
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (1)
- | フォームオブジェクトに、\ ``org.springframework.web.multipart.MultipartFile``\ のプロパティを定義する。
テンプレートHTMLの実装
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.. code-block:: html
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}|
.. list-table::
:header-rows: 1
:widths: 10 90
* - 項番
- 説明
* - | (1)
- | \ ``