我也对这个问题感兴趣。在 CXF 邮件列表中与 Sergey 讨论时,我了解到如果附件超过某个阈值,CXF 正在使用临时文件。
在此过程中,我发现了这个blogpost,它解释了如何安全地使用 CXF 附件。
您也可以对this page 上的示例感兴趣。
目前我只能说这些,我正在调查,希望对您有所帮助。
编辑:目前这是我们使用 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);
...
}
关于 sn-p 的几点说明:
-
@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,这就是为什么我们在上面的 sn-p 中写道:
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
}
}
但是请注意,已经进行了一些试验来了解通过了什么、如何以及热修复错误的方式(例如,附件部分的第一个标题的第一个字母是吃,所以你有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)