首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用JAX构建大型MTOM/XOP消息

使用JAX构建大型MTOM/XOP消息
EN

Stack Overflow用户
提问于 2009-12-16 17:44:01
回答 2查看 10.3K关注 0票数 6

关于如何在JAX中使用MTOM/XOP,我有一个问题。我正在编写一个发送大量二进制数据的web服务。客户端请求多个文件,服务器返回响应中的文件。

我能够让它正确地构建响应,以便它正确地实现XOP,但是我遇到了与内存相关的问题,因为它在发送之前将整个响应存储在内存中。这个web服务发送的文件可以使非常大(例如,giga-字节大),因此将响应存储在内存中不是一种选择。

这个甲骨文网站 (以及这一个)似乎解决了这个问题,但我只是不明白。我认为他们使用DataHandler对象来流请求/响应,但是我不知道他们是如何实例化它的。

我正在使用wsimport从现有的WSDL生成JAX类文件。我在Java 6中使用2.1.6。

如何在构建响应时发送响应,而不必首先存储在所有内存中?

提前谢谢你的帮助。

更新12/17:在保存二进制数据的WSDL中向模式元素添加了以下属性。这导致wsimport向JAXB类添加DataHandler对象。然后可以将一个FileDataHandler添加到响应中,而不是添加文件的全部内容,从而允许服务器流每个文件的内容,而不是将它们全部保存在内存中:

代码语言:javascript
复制
xmlns:xmime="http://www.w3.org/2005/05/xmlmime" 
xmime:expectedContentTypes="application/octet-stream"

因此,服务器现在就正确地构建响应,并且客户端在接收请求时正确地将每个文件保存到磁盘。但是,由于某些原因,客户端仍然将整个响应读入内存。

服务器代码(SIB):

代码语言:javascript
复制
@MTOM
@StreamingAttachment(parseEagerly = true, memoryThreshold = 4000000L) 
@WebService(...)
public class DownloadFilesPortTypeImpl implements DownloadFilesPortType {
 @Override
 public FileSetResponseType downloadFileSet(FileSetRequestType body) {
        FileSetResponseType response = new FileSetResponseType();
        for (FileRequest freq : body.getFileRequest()){
            try{
                //find the file on disk
                File file = findFile(freq.getFileId());

                //read the file data into memory
                byte[] fileData;
                {
                    FileInputStream in = new FileInputStream(file);
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    byte buf[] = new byte[8192];
                    int read;
                    while ((read = in.read(buf)) != -1){
                         out.write(buf, 0, read);
                    }
                    in.close();
                    out.close();
                    fileData = out.toByteArray();
                }

                //add the file to the response
                FileResponse fresp = new FileResponse();
                fresp.setFileId(freq.getFileId());
                fresp.setData(fileData); //<-- type "xs:base64Binary"
                response.getFileResponse().add(fresp);
            }
            catch (IOException e){
            }
        }

        return response;
 }
}

客户端代码:

代码语言:javascript
复制
DownloadFilesService service = new DownloadFilesService();
MTOMFeature mtomFeature = new MTOMFeature();
StreamingAttachmentFeature stf = new StreamingAttachmentFeature(null, true, 4000000L);
DownloadFilesPortType port = service.getDownloadFilesPortSoap12(mtomFeature, stf);

FileSetRequestType request = new FileSetRequestType();

FileRequest freq = new FileRequest();
freq.setFileId("1234");
request.getFileRequest().add(freq);

freq = new FileRequest();
freq.setFileId("9876");
request.getFileRequest().add(freq);

//...

FileSetResponseType response = port.downloadFileSet(request); //reads entire response into memory
for (FileResponse fres : response.getFileResponse()){
    byte[] data = fres.getFileData();
    //...
}
EN

回答 2

Stack Overflow用户

发布于 2009-12-16 18:02:56

您可以创建实现DataSource的自己的类,并构造传入它的DataHandler。甚至可以是匿名的。

在Apache中,我们可以更容易地做到这一点。您只需要一个返回DataSource或DataHandler的“getter”。您发布的代码中的详细方案对我来说并不熟悉。

我认为同样的方法适用于JDK的JAXB+JAXB。见

票数 2
EN

Stack Overflow用户

发布于 2011-07-19 08:36:13

由于JAX包含在Java 6 (Metro或其中的一部分)中,客户端代码必须设置HTTP块大小,以支持通过DataHandler InputStream传输大型MTOM附件。

代码语言:javascript
复制
((BindingProvider)port).getRequestContext()
   .put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);

但是目前它只是因为一个bug WS-936而不起作用。

因此,即使使用了适当的分块和DataHandler,在服务调用时,附件也会立即完全加载到内存中。我已经检查了网络流量:读取DataHandler输入流之前的完整内容传输,以及Java堆内存的使用情况:它已经增加到至少包含三个附件副本。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1916377

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档