我posted将此问题提交至CXF列表,没有任何运气。所以我们走了。我正在尝试将大文件上传到远程服务器(想想它们是虚拟机磁盘)。所以我有一个接受上传请求的宁静服务。上传的处理程序如下所示:
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/doupload")
public Response receiveStream(MultipartBody multipart) {
List<Attachment> allAttachments = body.getAllAttachments();
Attachment att = null;
for (Attachment b : allAttachments) {
if (UPLOAD_FILE_DESCRIPTOR.equals(b.getContentId())) {
att = b;
}
}
Assert.notNull(att);
DataHandler dh = att.getDataHandler();
if (dh == null) {
throw new WebApplicationException(HTTP_BAD_REQUEST);
}
try {
InputStream is = dh.getInputStream();
byte[] buf = new byte[65536];
int n;
OutputStream os = getOutputStream();
while ((n = is.read(buf)) > 0) {
os.write(buf, 0, n);
}
ResponseBuilder rb = Response.status(HTTP_CREATED);
return rb.build();
} catch (IOException e) {
log.error("Got exception=", e);
throw new WebApplicationException(HTTP_INTERNAL_ERROR);
} catch (NoSuchAlgorithmException e) {
log.error("Got exception=", e);
throw new WebApplicationException(HTTP_INTERNAL_ERROR);
} finally {}
}
此代码的客户端非常简单:
public void sendLargeFile(String filename) {
WebClient wc = WebClient.create(targetUrl);
InputStream is = new FileInputStream(new File(filename));
Response r = wc.post(new Attachment(Constants.UPLOAD_FILE_DESCRIPTOR,
MediaType.APPLICATION_OCTET_STREAM, is));
}
代码在功能方面运行良好。在性能方面,我注意到在我的处理程序(receiveStream()方法)获取流的第一个字节之前,整个流实际上被持久化到一个临时文件中(使用CachedOutputStream)。不幸的是,这对我来说是不可接受的。
鉴于所有这些,我正在寻找一种方法来使用HTTP InputStream(或尽可能接近它)直接从那里读取附件并在之后处理它。我猜这个问题转化为以下之一: - 有没有办法告诉CXF不要缓存 - 或者 - 是否有办法传递CXF输出流(我写的一个),而不是使用CachedOutputStream
我发现了一个类似的问题here。决议说使用CXF 2.2.3或更高版本,我使用2.4.4(并试用2.7.0)没有运气。
感谢。
答案 0 :(得分:2)
我认为这在逻辑上是不可能的(无论是在CXF还是其他任何地方)。您正在调用getAllAttachements()
,这意味着服务器应该从HTTP输入流中收集有关它们的信息。这意味着整个流必须进入内存进行MIME解析。
在您的情况下,您应该直接使用流,并自己进行MIME解析:
public Response receiveStream(InputStream input) {
现在您可以完全控制输入,并可以逐字节地将其消耗到内存中。
答案 1 :(得分:0)
我最终以一种不雅的方式修复问题,但它确实有效,所以我想分享我的经验。如果有一些“标准”或更好的方法,请告诉我。
由于我正在编写服务器端,因此我知道我按照发送顺序访问所有附件,并在流式传输时处理它们。因此,要反映处理程序方法的行为(receiveStream()方法上面),我在服务器端创建了一个名为“@SequentialAttachmentProcessing”的新注释,并使用它注释了我的上述方法。
另外,写了一个Attachment的子类,名为SequentialAttachment,其作用类似于链表。它有一个跳过当前附件的skip()方法,当附件结束时,hasMore()方法会告诉你是否有另一个附件。
然后我编写了一个自定义的multipart / form-data提供程序,其行为如下:如果目标方法如上所述,请处理附件,否则调用默认提供程序进行处理。当它由我的提供者处理时,它总是最多返回一个附件。因此,它可能会误导非怀疑的处理方法。但是,我认为这是可以接受的,因为服务器的编写者必须将方法注释为“@SequentialAttachmentProcessing”,因此必须知道这需要什么。
因此,receiveStream()方法的实现现在类似于:
@POST
@SequentialAttachmentProcessing
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/doupload")
public Response receiveStream(MultipartBody multipart) {
List<Attachment> allAttachments = body.getAllAttachments();
Assert.isTrue(allAttachments.size() <= 1);
if (allAttachment.size() > 0) {
Attachment head = allAttachments.get(0);
Assert.isTrue(head instanceof SequentialAttachment);
SequentialAttachment att = (SequentialAttachment) head;
while (att != null) {
DataHandler dh = att.getDataHandler();
InputStream is = dh.getInputStream();
byte[] buf = new byte[65536];
int n;
OutputStream os = getOutputStream();
while ((n = is.read(buf)) > 0) {
os.write(buf, 0, n);
}
if (att.hasMore()) {
att = att.next();
}
}
}
}
虽然这解决了我的直接问题,但我仍然认为必须有一种标准的方法来做到这一点。我希望这有助于某人。