web-dev-qa-db-ja.com

Jersey RESTful Webサービスの他のオブジェクトと一緒にファイルをアップロード

画像を従業員データとともにアップロードして、システムに従業員情報を作成したい。私はジャージを使用して、さまざまな休憩通話でそれを行うことができます。しかし、私は1回の休憩で達成したいです。構造の下に提供します。この点で行う方法を教えてください。

@POST
@Path("/upload2")
@Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response uploadFileWithData(
        @FormDataParam("file") InputStream fileInputStream,
        @FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
        Employee emp) {

//..... business login

}

実行しようとするたびに、Chrome郵便配達員でエラーが発生します。私の従業員jsonの単純な構造を以下に示します。

{
    "Name": "John",
    "Age": 23,
    "Email": "[email protected]",
    "Adrs": {
        "DoorNo": "12-A",
        "Street": "Street-11",
        "City": "Bangalore",
        "Country": "Karnataka"
    }
}

ただし、2つの異なる呼び出しを行うことでそれを行うことができますが、ファイルと従業員の実際のデータを受信できるように、1つの残りの呼び出しで達成したいです。

この点であなたに助けを求める。

54
Sambit

2つのContent-Typeを使用することはできません(技術的には以下で行っていますが、それらはマルチパートの各部分で分離されていますが、メインタイプはマルチパートです)。それは基本的にあなたのメソッドで期待していることです。メインメディアタイプとして、マルチパートandjsonを一緒に期待しています。 Employeeデータは、マルチパートの一部である必要があります。したがって、Employee@FormDataParam("emp")を追加できます。

@FormDataParam("emp") Employee emp) { ...

これが私がテストに使用したクラスです

@Path("/multipart")
public class MultipartResource {

    @POST
    @Path("/upload2")
    @Consumes({MediaType.MULTIPART_FORM_DATA})
    public Response uploadFileWithData(
            @FormDataParam("file") InputStream fileInputStream,
            @FormDataParam("file") FormDataContentDisposition cdh,
            @FormDataParam("emp") Employee emp) throws Exception{

        Image img = ImageIO.read(fileInputStream);
        JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
        System.out.println(cdh.getName());
        System.out.println(emp);

        return Response.ok("Cool Tools!").build();
    } 
}

まず、クライアントAPIでテストして、動作することを確認しました

@Test
public void testGetIt() throws Exception {

    final Client client = ClientBuilder.newBuilder()
        .register(MultiPartFeature.class)
        .build();
    WebTarget t = client.target(Main.BASE_URI).path("multipart").path("upload2");

    FileDataBodyPart filePart = new FileDataBodyPart("file", 
                                             new File("stackoverflow.png"));
    // UPDATE: just tested again, and the below code is not needed.
    // It's redundant. Using the FileDataBodyPart already sets the
    // Content-Disposition information
    filePart.setContentDisposition(
            FormDataContentDisposition.name("file")
                                    .fileName("stackoverflow.png").build());

    String empPartJson
            = "{"
            + "  \"id\": 1234,"
            + "  \"name\": \"Peeskillet\""
            + "}";

    MultiPart multipartEntity = new FormDataMultiPart()
            .field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
            .bodyPart(filePart);

    Response response = t.request().post(
            Entity.entity(multipartEntity, multipartEntity.getMediaType()));
    System.out.println(response.getStatus());
    System.out.println(response.readEntity(String.class));

    response.close();
}

テスト用にEmployeeおよびidフィールドを持つ単純なnameクラスを作成しました。これは完璧に機能します。画像を表示し、コンテンツの性質を印刷し、Employeeオブジェクトを印刷します。

私はPostmanにあまり詳しくないので、最後にテストを保存しました:-)

enter image description here

応答"Cool Tools"を確認できるように、正常に動作しているように見えます。しかし、印刷されたEmployeeデータを見ると、nullであることがわかります。これは、クライアントAPIでうまく機能したため、奇妙です。

プレビューウィンドウを見ると、問題がわかります

enter image description here

emp本文部分にContent-Typeヘッダーはありません。クライアントAPIで明示的に設定したことがわかります。

MultiPart multipartEntity = new FormDataMultiPart()
        .field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
        .bodyPart(filePart);

だから、これは本当に完全な答えの一部partだけだと思います。私が言ったように、私はPostmanに精通していないので、個々の身体部分にContent-Typesを設定する方法がわかりません。画像のimage/pngは、画像部分に対して自動的に設定されました(ファイル拡張子によって決定されただけだと思います)。これを理解できる場合は、問題を解決する必要があります。これを行う方法を見つけたら、回答として投稿してください。


完全を期すために...

基本構成:

依存:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
    <version>${jersey2.version}</version>
</dependency>

クライアント構成:

final Client client = ClientBuilder.newBuilder()
    .register(MultiPartFeature.class)
    .build();

サーバー構成:

// Create JAX-RS application.
final Application application = new ResourceConfig()
    .packages("org.glassfish.jersey.examples.multipart")
    .register(MultiPartFeature.class);

更新

したがって、Postmanクライアントからわかるように、一部のクライアントは、FormData(js)を使用する場合のデフォルト機能に関して、個々のパーツのContent-Typeを設定できません。これにはブラウザーも含まれます。

クライアントがこれを回避することを期待することはできません。したがって、できることは、データを受信するときに、デシリアライズする前にContent-Typeを明示的に設定することです。例えば

@POST
@Path("upload2")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@FormDataParam("emp") FormDataBodyPart jsonPart,
                                  @FormDataParam("file") FormDataBodyPart bodyPart) { 
     jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
     Employee emp = jsonPart.getValueAs(Employee.class);
}

POJOを入手するのは少し余分な作業ですが、クライアントに独自のソリューションを見つけさせようとするよりも優れたソリューションです。


側近

  • これらのコメント には会話があり、デフォルトのHttpUrlConnectionとは異なるコネクターを使用している場合に興味があるかもしれません。
86
Paul Samsotha

以下のコードを使用して、MULTIPART FORM DATAを使用してフォームから画像ファイルとデータにアクセスできます。

@POST
@Path("/UpdateProfile")
@Consumes(value={MediaType.APPLICATION_JSON,MediaType.MULTIPART_FORM_DATA})
@Produces(value={MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response updateProfile(
    @FormDataParam("file") InputStream fileInputStream,
    @FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
    @FormDataParam("ProfileInfo") String ProfileInfo,
    @FormDataParam("registrationId") String registrationId) {

    String filePath= "/filepath/"+contentDispositionHeader.getFileName();

    OutputStream outputStream = null;
    try {
        int read = 0;
        byte[] bytes = new byte[1024];
        outputStream = new FileOutputStream(new File(filePath));

        while ((read = fileInputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, read);
        }

        outputStream.flush();
        outputStream.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (outputStream != null) { 
            try {
                outputStream.close();
            } catch(Exception ex) {}
        }
    }
}
2
zameer

Peeskilletにコメントを追加したいのですが、50レピュテーションポイントを持っていないので、答えとして追加します。

Jerseyクライアント2.21.1で@peeskilletソリューションを試したところ、400のエラーがありました。クライアントコードに次のコードを追加すると機能しました。

  MediaType contentType = MediaType.MULTIPART_FORM_DATA_TYPE;
  contentType = Boundary.addBoundary(contentType);

  Response response = t.request().post(
        Entity.entity(multipartEntity, contentType));

リクエスト後の呼び出しでハードコードされたMediaType.MULTIPART_FORM_DATAの代わりに。

0
mkag

ApplicationConfigは、ファイルのアップロードを有効にするために、glassfish.jersey.media ..からMultiPartFeature.classを登録する必要があります

@javax.ws.rs.ApplicationPath(ResourcePath.API_ROOT)
public class ApplicationConfig extends ResourceConfig {  
public ApplicationConfig() {
        //register the necessary headers files needed from client
        register(CORSConfigurationFilter.class);
        //The jackson feature and provider is used for object serialization
        //between client and server objects in to a json
        register(JacksonFeature.class);
        register(JacksonProvider.class);
        //Glassfish multipart file uploader feature
        register(MultiPartFeature.class);
        //inject and registered all resources class using the package
        //not to be tempered with
        packages("com.flexisaf.safhrms.client.resources");
        register(RESTRequestFilter.class);
    }
0
pitaside