我在JAX-WS中有一个Java Web服务,它从另一个方法返回一个OutputStream。我似乎无法弄清楚如何将OutputStream流式传输到返回的DataHandler,而不是创建一个临时文件,写入它,然后再将它作为InputStream重新打开。这是一个例子:
@MTOM
@WebService
class Example {
@WebMethod
public @XmlMimeType("application/octet-stream") DataHandler service() {
// Create a temporary file to write to
File fTemp = File.createTempFile("my", "tmp");
OutputStream out = new FileOutputStream(fTemp);
// Method takes an output stream and writes to it
writeToOut(out);
out.close();
// Create a data source and data handler based on that temporary file
DataSource ds = new FileDataSource(fTemp);
DataHandler dh = new DataHandler(ds);
return dh;
}
}
主要问题是writeToOut()方法可以返回远大于计算机内存的数据。这就是为什么该方法首先使用MTOM - 流式传输数据。我似乎无法绕过如何直接从OutputStream传输数据,我需要提供给返回的DataHandler(最终是接收StreamingDataHandler的客户端)。
我尝试过使用PipedInputStream和PipedOutputStream,但这些似乎并不是我需要的,因为在写入PipedOutputStream之后需要返回DataHandler。
有什么想法吗?
答案 0 :(得分:4)
我找到了答案,就像Christian所说的那样(创建一个执行writeToOut()的新线程):
@MTOM
@WebService
class Example {
@WebMethod
public @XmlMimeType("application/octet-stream") DataHandler service() {
// Create piped output stream, wrap it in a final array so that the
// OutputStream doesn't need to be finalized before sending to new Thread.
PipedOutputStream out = new PipedOutputStream();
InputStream in = new PipedInputStream(out);
final Object[] args = { out };
// Create a new thread which writes to out.
new Thread(
new Runnable(){
public void run() {
writeToOut(args);
((OutputStream)args[0]).close();
}
}
).start();
// Return the InputStream to the client.
DataSource ds = new ByteArrayDataSource(in, "application/octet-stream");
DataHandler dh = new DataHandler(ds);
return dh;
}
}
由于final
变量,它有点复杂,但据我所知,这是正确的。当线程启动时,它会在第一次尝试调用out.write()
时阻塞;同时,输入流返回给客户端,客户端通过读取数据来解锁写入。 (我之前实现此解决方案的问题是我没有正确关闭流,因此遇到错误。)
答案 1 :(得分:3)
抱歉,我只为C#而不是java做过这个,但我认为你的方法应该启动一个线程来运行“writeToOut(out);”在parralel。您需要创建一个特殊的流并将其传递给新线程,该线程将该流提供给writeToOut。启动线程后,将该stream-object返回给调用者。
如果你只有一个写入流并且之后返回的方法和另一个使用流并且之后返回的方法,则没有别的办法。
最重要的是要抓住这样一个多线程的安全流:如果内部缓冲区太满,它将阻塞每一方。
不知道Java-pipe-stream是否适用于此。
答案 2 :(得分:1)
包装图案? : - 。)
自定义javax.activation.DataSource实现(只有4种方法)能够做到这一点吗?
return new DataHandler(new DataSource() {
// implement getOutputStream to return the stream used inside writeToOut()
...
});
我没有可用的IDE来测试这个,所以我只是在做一个建议。我还需要writeToOut总体布局:-)。
答案 3 :(得分:0)
在我的应用程序中,我使用InputStreamDataSource实现,它将InputStream作为构造函数参数而不是FileDataSource中的File。它到目前为止工作。
public class InputStreamDataSource implements DataSource {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private final String name;
public InputStreamDataSource(InputStream inputStream, String name) {
this.name = name;
try {
int nRead;
byte[] data = new byte[16384];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String getContentType() {
return new MimetypesFileTypeMap().getContentType(name);
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(buffer.toByteArray());
}
@Override
public String getName() {
return name;
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new IOException("Read-only data");
}
}