处理 CXF API 中的多部分附件

2024-04-18

我正在尝试使用 Apache CXF 开发一个 API 调用,该调用会随请求一起接收附件。我跟着this http://cxf.apache.org/docs/jax-rs-multiparts.html教程,这就是我到目前为止所得到的。

@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 是否已将其存储为文件?如果是这样,我可以继续使用它。有什么建议么?


我对这个问题也很感兴趣。在 CXF 邮件列表上与 Sergey 讨论时,我了解到如果附件超过某个阈值,CXF 就会使用临时文件。

在这个过程中我发现了这一点blogpost http://ext2xhb.wordpress.com/2011/06/08/using-cxf-attachment-safely/这解释了如何安全使用 CXF 附件。 您可能会对以下示例感兴趣这一页 https://cwiki.apache.org/confluence/display/CXF20DOC/JAX-RS+Multiparts#JAX-RSMultiparts-Writingattachments以及。

目前我能说的就是这些,因为我正在调查,希望对您有所帮助。


编辑:目前,这是我们如何使用 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);

我们还发现在帮助下谢尔盖·别廖兹金我们可以转换或包装它InputStream,这就是为什么我们在上面的代码片段中写道:

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

IncomingFile是我们的定制包装InputStream,为此你必须注册一个MessageBodyReader, ParamHandler不会有帮助,因为它们不与流一起使用,而是与String.

@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
  }
}

但请注意,已经进行了一些试验来了解通过的内容、方式以及热修复错误的方式(例如附件部分第一个标题的第一个字母是 eat,所以你有ontent-Type代替Content-Type).

当然entityStream代表实际的InputStream的附件。该流将从内存或磁盘读取数据,具体取决于 CXF 放置数据的位置;有一个大小阈值属性(attachment-memory-threshold) 对于这个问题。您还可以指定临时附件的去向(attachment-directory).

只是不要忘记关闭流当你完成时(有些工具会帮你完成)。

配置完所有内容后,我们对其进行了测试放心 https://code.google.com/p/rest-assured/ from 约翰·哈勒比. (尽管有些代码是我们测试实用程序的一部分) :

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执行请求时将标头添加到相关部分。

值得一提的是,可以将所有部分放在一个列表中,只需编写一个具有单个类型参数的方法List<Attachment>。不过,我更喜欢在方法签名中包含实际参数,因为它更干净且更少样板文件。

@POST
void takeAllParts(List<Attachment> attachments)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

处理 CXF API 中的多部分附件 的相关文章

随机推荐