4

リクエストとともに添付ファイルを受け取る Apache CXF を使用して API 呼び出しを開発しようとしています。私はこのチュートリアルに従いましたが、これは私がこれまでに得たものです。

@POST
@Path("/upload")
@RequireAuthentication(false)
public Response uploadWadl(MultipartBody multipartBody){
    List<Attachment> attachments = multipartBody.getAllAttachments();
    DataHandler dataHandler = attachments.get(0).getDataHandler();
    try {
        InputStream is = dataHandler.getInputStream();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return Response("OK");
}

添付ファイルに InputStream オブジェクトを取得していますが、すべて正常に動作しています。ただし、添付ファイルを java.io.File オブジェクトとして別の関数に渡す必要があります。ここでファイルを作成し、入力ストリームから読み取って書き込むことができることはわかっています。しかし、より良い解決策はありますか?CXF はすでにファイルとして保存していますか? もしそうなら、私は先に進んでそれを使うことができます。助言がありますか?

4

1 に答える 1

15

私もこの件に興味があります。CXF メーリング リストで Sergey と議論しているときに、添付ファイルが特定のしきい値を超えている場合、CXF が一時ファイルを使用していることを知りました。

その過程で、CXF アタッチメントを安全に使用する方法を説明しているこのブログ投稿を見つけました。このページの例にも興味があります。

現在調査中のため、現時点で言えることはこれだけです。参考になれば幸いです。


編集: 現時点では、CXF 2.6.x でアタッチメントを処理する方法を次に示します。マルチパート コンテンツ タイプを使用したファイルのアップロードについて。

REST リソースでは、次のメソッドを定義しました。

  @POST
  @Produces(MediaType.APPLICATION_JSON)
  @Consumes(MediaType.MULTIPART_FORM_DATA)
  @Path("/")
  public Response archive(
          @Multipart(value = "title", required = false) String title,
          @Multipart(value = "hash", required = false) @Hash(optional = true) String hash,
          @Multipart(value = "file") @NotNull Attachment attachment) {

    ...

    IncomingFile incomingFile = attachment.getObject(IncomingFile.class);

    ...
  }

そのスニペットに関するいくつかのメモ:

  • @Multipartは JAXRS の標準ではなく、JAXRS 2 にもありません。CXF の一部です。
  • 私たちのコードでは、Bean 検証を実装しています (JAXRS 1 で自分で行う必要があります)。
  • を使用する必要はありません。MultipartBodyここで重要なのは、タイプの引数を使用することですAttachment

したがって、私たちが知る限り、メソッド シグネチャで必要な型を直接取得する可能性はまだありません。たとえばInputStream、添付ファイルのみが必要な場合は、メソッドの署名に入れることはできません。タイプを使用してorg.apache.cxf.jaxrs.ext.multipart.Attachment、次のステートメントを記述する必要があります。

InputStream inputStream = attachment.getObject(InputStream.class);

また、Sergey Beryozkinの助けを借りて、これを変換またはラップできることを発見しました。これInputStreamが、上記のスニペットで次のように記述した理由です。

IncomingFile incomingFile = attachment.getObject(IncomingFile.class);

IncomingFileは、 のカスタム ラッパーです。そのためには、InputStreamを登録する必要があります。ストリームでは機能せず、 では機能しないため、役に立ちません。MessageBodyReaderParamHandlerString

@Component
@Provider
@Consumes
public class IncomingFileAttachmentProvider implements MessageBodyReader<IncomingFile> {
  @Override
  public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return type != null && type.isAssignableFrom(IncomingFile.class);
  }

  @Override
  public IncomingFile readFrom(Class<IncomingFile> type,
                              Type genericType,
                              Annotation[] annotations,
                              MediaType mediaType,
                              MultivaluedMap<String, String> httpHeaders,
                              InputStream entityStream
  ) throws IOException, WebApplicationException {

    return createIncomingFile(entityStream, fixedContentHeaders(httpHeaders)); // the code that will return an IncomingFile
  }
}

ただし、渡されたもの、その方法、およびバグをホットフィックスする方法を理解するための試行がいくつかあることに注意してください(たとえば、添付ファイル部分の最初のヘッダーの最初の文字は、 のontent-Type代わりにeat so you had でしたContent-Type)

もちろん、は添付ファイルentityStreamの実際を表します。InputStreamこのストリームは、CXF がデータを置く場所に応じて、メモリまたはディスクからデータを読み取ります。そのためのサイズしきい値プロパティ ( attachment-memory-threshold) があります。一時的な添付ファイルの移動先を指定することもできます ( attachment-directory)。

完了したら、ストリームを閉じることを忘れないでください(いくつかのツールがそれを行います)。

すべてが構成されたら、Johan Halebyの Rest-Assuredでテストしました。(ただし、一部のコードはテスト ユーティリティの一部です) :

given().log().all()
        .multiPart("title", "the.title")
        .multiPart("file", file.getName(), file.getBytes(), file.getMimeType())
.expect().log().all()
        .statusCode(200)
        .body("store_event_id", equalTo("1111111111"))
.when()
        .post(host().base().endWith("/store").toStringUrl());

または、そのような方法でcurl経由でファイルをアップロードする必要がある場合:

curl --trace -v -k -f
     --header "Authorization: Bearer b46704ff-fd1d-4225-9dd4-e29065532b73"
     --header "Content-Type: multipart/form-data"
     --form "hash={SHA256}3e954efb149aeaa99e321ffe6fd581f84d5a497b6fab5c86e0d5ab20201f7eb5"
     --form "title=fantastic-video.mp4"
     --form "archive=@/the/path/to/the/file/fantastic-video.mp4;type=video/mp4"
     -X POST http://localhost:8080/api/video/event/store

この回答を締めくくるために、JSON ペイロードをマルチパートにすることが可能であることを言及したいと思います。そのためAttachment、署名でタイプを使用してから書き込むことができます。

Book book = attachment.getObject(Book.class)

または、次のような引数を記述できます。

@Multipart(value="book", type="application/json") Book book

Content-Typeリクエストを実行するときに、関連する部分にヘッダーを追加することを忘れないでください。

type の単一の引数を持つメソッドを記述するだけで、すべての部分をリストに含めることが可能であると言う価値があるかもしれませんList<Attachment>。ただし、メソッド シグネチャに実際の引数を含めることを好みます。これは、簡潔でボイラープレートが少ないためです。

@POST
void takeAllParts(List<Attachment> attachments)
于 2012-11-06T14:26:06.007 に答える