6

JAX-WS での MTOM/XOP の使用について質問があります。大量のバイナリ データを送信する Web サービスを作成しています。クライアントは多数のファイルを要求し、サーバーは応答でファイルを返します。

XOPを正しく実装するように応答を正しく構築することはできますが、送信する前に応答全体をメモリに保存するため、メモリ関連の問題が発生します。この Web サービスが送信するファイルは非常に大きくなる可能性があるため (ギガバイト単位など)、応答をメモリに格納することはできません。

このオラクルのウェブサイト(およびこれと一緒に)はこの問題を解決しているようですが、私はそれを理解していません. オブジェクトを使用してリクエスト/レスポンスをストリーミングしていると思いますDataHandlerが、インスタンス化する方法がわかりません。

を使用して、既存の WSDL から JAX-WS クラス ファイルを生成していますwsimport。Java 6 で JAX-WS RI 2.1.6 を使用しています。

最初にすべてをメモリに保存することなく、ビルド中に応答を送信するにはどうすればよいですか?

よろしくお願いします。


UPDATE 12/17:バイナリ データを保持する WSDL のスキーマ要素に次の属性を追加しました。これにより、オブジェクトが JAXB クラスwsimportに追加されます。DataHandler次に、ファイルの内容全体を追加する代わりに、応答にAFileDataHandlerを追加して、サーバーが各ファイルの内容をすべてメモリに保持する代わりにストリーミングできるようにします。

xmlns:xmime="http://www.w3.org/2005/05/xmlmime" 
xmime:expectedContentTypes="application/octet-stream"

そのため、サーバーは応答を正しく作成し、クライアントは要求を受信したときに各ファイルをディスクに適切に保存します。ただし、何らかの理由で、クライアントは引き続き応答全体をメモリに読み込みます。


サーバーコード (SIB):

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

クライアントコード:

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();
    //...
}
4

2 に答える 2

2

Java 6 (Metro またはその一部) に含まれる JAX-WS RI では、DataHandler InputStream を介して大きな MTOM 添付ファイルをストリーミングできるように、クライアント コードで HTTP チャンク サイズを設定する必要があります。

((BindingProvider)port).getRequestContext()
   .put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);

しかし、現時点では、バグJAX_WS-936のために機能しません

その結果、チャンク化と DataHandler の使用が適切であっても、添付ファイルはサービスの呼び出し時にすぐにメモリに完全に読み込まれます。ネットワーク トラフィックを確認しました: DataHandler 入力ストリームを読み取る前に完全なコンテンツが転送され、Java ヒープ メモリの使用量: 少なくとも 3 つの添付ファイルのコピーを含むように増加しました。

于 2011-07-19T08:36:13.313 に答える
2

それを渡す DataHandler を実装DataSourceおよび構築する独自のクラスを作成します。匿名にすることもできます。

Apache CXF では、これをより簡単に行うことができます。DataSource または DataHandler を返す 'getter' を使用できます。あなたが投稿したコードの精巧なスキームは、私にはなじみのないものです。

JDKのJAX-WS+JAXBでも同様の方法が使えると思います。これを参照してください。

于 2009-12-16T18:02:56.370 に答える