【Jersey】ファイルアップロードの実装方法の紹介

Jerseyを使用してファイルアップロードの実装をするときは、次の点に注意する必要があります。

  1. アプリケーションクラスにMultiPartFeatureクラスを登録する。
  2. リソースクラスに@Consumesアノテーションを設定する。
  3. リソースクラスのパラメータに@FormDataParamアノテーション、または、MultiPartクラスを使用する。

今回は、注意点の実装方法と検証時に発生したエラー内容を紹介していきます。

スポンサーリンク

検証環境

検証に使用した環境/ライブラリを以下に記載します。

  • Java
    • バージョン:11
  • Jersey
    • バージョン:2.31
  • Tomcat
    • バージョン:8.5.49
  • Jetty
    • 9.4.24.v20191120
  • Gradle
    • バージョン:6.3

ビルドスクリプト

検証に使用したビルドスクリプトを次に記載します。

ファイルアップロードのマルチパートデータを処理をするため、jersey-media-multipartライブラリが必要になります。

build.gradle

plugins {
  id 'java'
  id 'org.gretty' version '3.0.3'
}

sourceCompatibility = '11'
targetCompatibility = '11'

compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'

repositories {
   jcenter()
}

dependencies {
  // jersey
  implementation 'org.glassfish.jersey.containers:jersey-container-servlet:2.31'
  implementation 'org.glassfish.jersey.inject:jersey-hk2:2.31'
  implementation 'org.glassfish.jersey.media:jersey-media-multipart:2.31'
  // xml
  implementation 'javax.xml.bind:jaxb-api:2.3.1'
  implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.3'
}

ライブラリ登録

アプリケーションでマルチパートデータを処理するためのライブラリを登録します。ResourceConfigの継承クラスのコンストラクタで次のように指定します。

this.register(MultiPartFeature.class);

【コード例】

package com.fumidzuki.example;

import javax.ws.rs.ApplicationPath;

import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("/")
public class Application extends ResourceConfig {
  public Application() {
    this.register(MultiPartFeature.class);
    this.packages(this.getClass().getPackage().getName());
  }
}

ライブラリを登録しないでマルチパートデータを処理するとエラーになることがあります。検証時には、次のエラーが発生しました。

Tomcatサーバの起動後にリソースクラスにアクセスすると次のエラーが発生します。

500 – Internal Server Error
java.lang.IllegalStateException: The resource configuration is not modifiable in this context

Jettyサーバの起動時に次のエラーが発生することがあります。

org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization

リソースクラスアクセス時に、次のHTTPステータスコードが返却されます。

415 Unsupported Media Type

リソースクラス作成

リソースクラスを作成してマルチパートデータを処理します。実装する方法には次の選択肢があります。

  1. @FormDataParamアノテーションを使用する。
  2. MultiPartクラスを使用する。

@FormDataParamアノテーション

リソースクラスのパラメータに@FormDataParamアノテーションを使用します。実装する方法は複数ありますが、どちらを選択してもファイル情報を取得できます。

FormDataContentDispositionとInputStream

パラメータにFormDataContentDispositionとInputStreamの両方を使用します。指定したクラスには次の情報が設定されます。

  • FormDataContentDisposition
    • ファイル情報を取得することができます。ファイル名称などを取得することができます。
  • InputStream
    • ストリーム情報を取得することができます。

【コード例】

@POST
@Path("/form-param")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFormDataParam(
    @FormDataParam("file") FormDataContentDisposition dispoosition,
    @FormDataParam("file") InputStream is) {
  String fileName = dispoosition.getFileName();
  return Response.ok(fileName).build();
}

ファイル名称を取得する場合は、FormDataContentDisposition.getFileNameを使用することで取得できます。

FormDataBodyPart

パラメータにFormDataBodyPartクラスを使用します。指定したクラスには、次の情報が設定されます。

  • FormDataBodyPart
    • ファイル情報/ストリーム情報を取得するために使用します。

【コード例】

@POST
@Path("/bodypart")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadDataBodyPart(@FormDataParam("file") FormDataBodyPart bodyPart) {
  ContentDisposition disposition = bodyPart.getContentDisposition();
  String fileName = disposition.getFileName();
  // 「getValueAs」、「getEntityAs」を内部的に使用しているためどちらを使用しても同じ動作になります。
  // InputStream is = bodyPart.getValueAs(InputStream.class);
  // InputStream is = bodyPart.getEntityAs(InputStream.class);
  return Response.ok(fileName).build();
}

ファイル名称を取得する場合は、FormDataBodyPart.getContentDisposition.getFileNameを使用することで取得できます。

ストリーム情報を取得する場合は、FormDataBodyPart.getEntityAs(InputStream.class)を使用することで取得できます。

似た機能としてFormDataBodyPart.getValueAsがあります。内部的にはBodyPart.getEntityAsを使用しています。

MultiPart

パラメータにMultiPartクラスを使用します。指定したクラスには、次の情報が設定されます。

  • MultiPart
    • ファイル情報/ストリーム情報を取得するために使用します。

【コード例】

@POST
@Path("/multipart")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFormDataMultiPart(MultiPart multiPart) {
  List<BodyPart> bodyParts = multiPart.getBodyParts();
  BodyPart bodyPart = bodyParts.get(0);
  ContentDisposition disposition = bodyPart.getContentDisposition();
  String fileName = disposition.getFileName();
  // InputStream is = bodyPart.getEntityAs(InputStream.class);
  return Response.ok(fileName).build();
}

MultiPart.getBodyPartsを使用することで情報を取得できます。戻り値がList形式のため、複数ファイルのアップロードに対応することもできます。

ファイル名称を取得する場合は、BodyPart.getContentDisposition.getFileNameを使用することで取得できます。

ストリーム情報を取得する場合は、BodyPart.getEntityAsを使用することで取得できます。

FormDataMultiPartクラスを使用することもできます。

サンプルプログラム

ファイルアップロードするための画面を含めたサンプルプログラムが次にありますので参考にしてみてください。

GitHub - fumidzuki/example-jersey-multipart
Contribute to fumidzuki/example-jersey-multipart development by creating an account on GitHub.

まとめ

Jerseyを使用してファイルアップロードするための方法を紹介しました。

参考

Jerseyのマルチパートについては、次の公式サイトが参考になります。

Chapter 9. Support for Common Media Type Representations