2015年7月27日月曜日

Spring でファイルアップロードを PUT メソッドで処理する方法

概要

Spring で multipart/form-data + MultipartFile を扱う場合は、通常は POST メソッドでなければなりません
どうやら MultipartFile は POST メソッドでないと操作できないようです
実装上そもそも不要なケースが多いですが、PUT メソッドで multipart/form-data を受け取る方法を紹介します

環境

  • Mac OS X 10.10.4
  • Eclipse Luna 4.4.1
  • Spring Framework 4.1.6
  • Spring Tool Suite 3.6.4
  • Java 1.8.0_31
  • Maven 3.2.1

サンプルプロジェクト作成

過去の記事を参考に作成してください
Spring Framework のバージョンが最新でない場合は pom.xml 「org.springframework-version」のバージョン記載部分を最新のバージョンに変更してください

コーディング

@RequestMapping に PUT で multipart/form-data を受け取る定義を記載する

@RequestMapping(value = "/**", method = {RequestMethod.PUT}, headers = "content-type=multipart/form-data")
@ResponseBody
public void receivePutMulti(@RequestParam(value = "file", required = true) MultipartFile file) {
  ...
}

受け取りたいURLマッピングに上記のようにメソッド名とヘッダーのタイプを指定します
今回はファイルのアップロードを想定しているため MultipartFile を受け取るように指定します
返り値は jsp を返却したい場合は String 型を返り値に指定し@ResponseBodyを削除してください

ちなみにこの状態で以下のように PUT メソッドでファイルのアップロードを実行しようとすると500エラーになります

curl -v -X PUT -H "Content-Type: multipart/form-data" http://localhost:8080/test/put/ -F "file=@pom.xml"
java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?

このエラーがでないように設定していきます

servlet-context.xml に独自 MultipartResolver を指定する

<beans:bean id="multipartResolver" class="com.sample.test.ExtendedMultipartResolver">
</beans:bean>

Multipart なデータは MultipartResolver というクラスでどう受け取るかのルール付けがされており、このクラスを拡張することで PUT メソッドでも MultipartFile が受信できるようにします

次に指定した独自の MultipartResolver クラスを作成していきます

独自 MultipartResolver クラスの作成

pom.xml に定義を追加する

<!-- FileUpload -->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>

独自の MultipartResolver を作成するのに commons-fileupload が必要になるので pom.xml に追記します
既存の<dependencies>タグ内に追記してください

ExtendedMultipartResolver クラスの作成

今回はcom.sampe.test.ExtendedMultipartResolverとして作成します
とりあえず以下がサンプルになります

package com.sample.test;

import javax.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

public class ExtendedMultipartResolver extends CommonsMultipartResolver {

    @Override
    public boolean isMultipart(HttpServletRequest request) {
        if (request != null) {
            String httpMethod = request.getMethod().toLowerCase();
            // test for allowed methods here...
            String contentType = request.getContentType();
            return (contentType != null && contentType.toLowerCase().startsWith("multipart"));
        } else {
            return false;
        }
    }
}

ポイントは@OverrideしているisMultipartメソッドでこの中でHttpServletRequestからアクセスしているメソッド情報を取得して PUT メソッドでもtrueを返却するように実装し直します
サンプルは全メソッドで multipart/form-data を許可する設定になっており特にメソッド名に応じてfalseを返却するような if 分岐は入れていません

動作確認

ここまで実装できたらアプリを再起動してコードを反映しましょう
先ほどの PUT + multipart/form-data の curl のサンプルリクエストを投げてみましょう

すると先ほどのエラーにはならず定義したメソッド内の処理が実行されていることが確認できると思います

最後に

ちょっと調べてみたんですが Spring がデフォルトで実装していないのは RFC 的にそうなっているため(?) みたいな記事があったような気がします
詳細には調べていないので、わかりませんが PUT で multipart/form-data は基本は受け取らないんですかね
そもそもそれは実装が悪いってことになるのか

参考サイト

0 件のコメント:

コメントを投稿