我对这个问题也很感兴趣。在CXF邮件列表上与Sergey讨论时,我了解到如果附件超过某个阈值,CXF将使用临时文件。
在这个过程中,我发现了这篇博客文章,它解释了如何安全地使用CXF附件。
您也可以看一下此页面上的示例。
目前我正在进行调查,这就是我能说的全部,希望能有所帮助。
编辑:目前我们在CXF 2.6.x中处理附件的方式如下。关于使用多部分内容类型上传文件的方法,请参见以下代码:
@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
,这就是为什么在上面的片段中我们写道:
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
包装器,您需要注册一个 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));
}
}
请注意,已经进行了一些试验以了解传递的内容,方法以及修补错误的方式。(例如附件部分的第一个标题的第一个字母是“e”,因此您有ontent-Type
而不是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
头部添加到相关部分即可。
值得一提的是,可以将所有部分放在列表中,只需编写一个类型为List<Attachment>
的单参数方法。但我更喜欢在方法签名中使用实际参数,因为这样更清晰,没有样板代码。
@POST
void takeAllParts(List<Attachment> attachments)