在CXF API中处理多部分附件 [英] Handling multipart attachments in CXF APIs

查看:118
本文介绍了在CXF API中处理多部分附件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Apache CXF开发一个API调用,该API随请求一起包含附件.我遵循了教程,这是我到目前为止所学到的. /p>

I am trying to develop an API call using Apache CXF that takes in an attachment along with the request. I followed this tutorial and this is what I have got so far.

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

I am getting an InputStream object to the attachment and everything is working fine. However I need to pass the attachment as a java.io.File object to another function. I know I can create a file here, read from the inputstream and write to it. But is there a better solution? Has the CXF already stored it as a File? If so I could just go ahead and use that. Any suggestions?

推荐答案

我对此事也很感兴趣.在与Sergey讨论CXF邮件列表时,我了解到,如果附件超过一定阈值,则CXF正在使用临时文件.

I'm also interested on this matter. While discussing with Sergey on the CXF mailing list, I learned that CXF is using a temporary file if the attachment is over a certain threshold.

在此过程中,我发现了这个博客文章解释了如何安全使用CXF附件. 例如,您可能对此页面.

In the process I discovered this blogpost that explains how to use CXF attachment safely. You can be interested by the exemple on this page as well.

这就是我目前正在调查中的全部内容,希望对您有所帮助.

That's all I can say at the moment as I'm investigating right now, I hope that helps.

目前,这是我们使用CXF 2.6.x处理附件的方式.关于使用多部分内容类型上传文件.

EDIT : At the moment here's how we handle attachment with CXF 2.6.x. About uploading a file using multipart content type.

在我们的REST资源中,我们定义了以下方法:

In our REST resource we have defined the following method :

  @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);

    ...
  }

关于该代码段的一些注释:

A few notes on that snippet :

  • @Multipart不是JAXRS的标准,甚至在JAXRS 2中也不是,它是CXF的一部分.
  • 在我们的代码中,我们实现了bean验证(您必须在JAXRS 1中自行完成)
  • 您不必使用MultipartBody,这里的关键是使用类型为Attachment
  • 的参数
  • @Multipart is not standard to JAXRS, it's not even in JAXRS 2, it's part of CXF.
  • In our code we have implemented bean validation (you have to do it yourself in JAXRS 1)
  • You don't have to use a MultipartBody, the key here is to use an argument of type Attachment

因此,据我们所知,尚不可能直接在方法签名中获取所需的类型.因此,例如,如果您只想要附件的InputStream,则不能将其放在方法的签名中.您必须使用org.apache.cxf.jaxrs.ext.multipart.Attachment类型并编写以下语句:

So yes as far as we know there is not yet a possibility to get directly the type we want in the method signature. So for example if you just want the InputStream of the attachment you cannot put it in the signature of the method. You have to use the org.apache.cxf.jaxrs.ext.multipart.Attachment type and write the following statement :

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

我们还发现,在 Sergey Beryozkin 的帮助下,我们可以变换或包装InputStream,这就是我们在上面的代码段中写道的原因:

Also we discovered with the help of Sergey Beryozkin that we could transform or wrap this InputStream, that's why in the above snippet we wrote :

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

IncomingFile是我们围绕InputStream的自定义包装,因为您必须注册MessageBodyReaderParamHandler不会帮助您,因为它们不适用于流,而只能用于String.

IncomingFile is our custom wrapper around the InputStream, for that you have to register a MessageBodyReader, ParamHandler won't help as they don't work with streams but with 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
  }
}

但是请注意,已经进行了一些试验来了解通过了什么,如何以及如何修复错误(例如,附件部分第一个标头的第一个字母被吃掉了,所以您有ontent-Type代替Content-Type).

Note however that there have been a few trials to understand what was passed, how, and the way to hot-fix bugs (For example the first letter of the first header of the attachment part was eat so you had ontent-Type instead of Content-Type).

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

Of course the entityStream represents the actual InputStream of the attachment. This stream will read data either from memory or from disk, depending on where CXF put the data ; there is a size threshold property (attachment-memory-threshold) for that matter. You can also say where the temporary attachments will go (attachment-directory).

请别忘了在完成操作后关闭流(某些工具会帮您完成该操作).

Just don't forget to close the stream when you are done (some tool do it for you).

配置完所有内容后,我们将使用中的 Rest-Assured 对其进行测试Johan Haleby . (尽管有些代码是我们的测试实用程序的一部分):

Once everything was configured we tested it with Rest-Assured from Johan Haleby. (Some code are part of our test utils though) :

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通过这种方式上传文件:

Or if you need to upload the file via curl in such a way :

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类型,然后编写

To finish this answer, I'd like to mention it is possible to have JSON payload in multipart, for that you can use an Attachment type in the signature and then write

Book book = attachment.getObject(Book.class)

或者您可以编写一个类似

Or you can write an argument like :

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

执行请求时,别忘了将Content-Type标头添加到相关部分.

Just don't forget to add the Content-Type header to the relevant part when performing the request.

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

It might be worth to say that it is possible to have all the parts in a list, just write a method with a single argument of type List<Attachment>. However I prefer to have the actual arguments in the method signature as it's cleaner and less boilerplate.

@POST
void takeAllParts(List<Attachment> attachments)

这篇关于在CXF API中处理多部分附件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆