简体   繁体   English

如何将OutputStream传递给StreamingDataHandler?

[英]How can you pipe an OutputStream to a StreamingDataHandler?

I've got a Java web service in JAX-WS that returns an OutputStream from another method. 我在JAX-WS中有一个Java Web服务,它从另一个方法返回一个OutputStream。 I can't seem to figure out how to stream the OutputStream into the returned DataHandler any other way than to create a temporary file, write to it, then open it back up again as an InputStream. 我似乎无法弄清楚如何将OutputStream流式传输到返回的DataHandler,而不是创建一个临时文件,写入它,然后再将它作为InputStream重新打开。 Here's an example: 这是一个例子:

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

The main issue is that the writeToOut() method can return data that are far larger than the computer's memory. 主要问题是writeToOut()方法可以返回远大于计算机内存的数据。 That's why the method is using MTOM in the first place - to stream the data. 这就是为什么该方法首先使用MTOM - 流式传输数据。 I can't seem to wrap my head around how to stream the data directly from the OutputStream that I need to provide to the returned DataHandler (and ultimately the client, who receives the StreamingDataHandler). 我似乎无法解决如何直接从OutputStream流式传输数据,我需要提供给返回的DataHandler(最终是接收StreamingDataHandler的客户端)。

I've tried playing around with PipedInputStream and PipedOutputStream, but those don't seem to be quite what I need, because the DataHandler would need to be returned after the PipedOutputStream is written to. 我已经尝试过使用PipedInputStream和PipedOutputStream,但这些似乎并不是我需要的,因为在写入PipedOutputStream之后需要返回DataHandler。

Any ideas? 有任何想法吗?

I figured out the answer, along the lines that Christian was talking about (creating a new thread to execute writeToOut()): 我找到了答案,就像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;
    }
}

It is a tad more complex due to final variables, but as far as I can tell this is correct. 由于final变量,它有点复杂,但据我所知,这是正确的。 When the thread is started, it blocks when it first tries to call out.write() ; 线程启动时,它会在第一次尝试调用out.write()时阻塞。 at the same time, the input stream is returned to the client, who unblocks the write by reading the data. 同时,输入流返回给客户端,客户端通过读取数据来解锁写入。 (The problem with my previous implementations of this solution was that I wasn't properly closing the stream, and thus running into errors.) (我之前实现此解决方案的问题是我没有正确关闭流,因此遇到错误。)

Sorry, I only did this for C# and not java, but I think your method should launch a thread to run "writeToOut(out);" 对不起,我只为C#而不是java做过这个,但我认为你的方法应该启动一个线程来运行“writeToOut(out);” in parralel. 在parralel。 You need to create a special stream and pass it to the new thread which gives that stream to writeToOut. 您需要创建一个特殊的流并将其传递给新线程,该线程将该流提供给writeToOut。 After starting the thread you return that stream-object to your caller. 启动线程后,将该stream-object返回给调用者。

If you only have a method that writes to a stream and returns afterwards and another method that consumes a stream and returns afterwards, there is no other way. 如果您只有一个写入流并且之后返回的方法以及另一个使用流并且之后返回的方法,则没有其他方法。

Of coure the tricky part is to get hold of such a -multithreading safe- stream: It shall block each side if an internal buffer is too full. 然而,棘手的部分是获得这样一个多线程安全流:如果内部缓冲区太满,它将阻塞每一方。

Don't know if a Java-pipe-stream works for that. 不知道Java管道流是否适用于此。

Wrapper pattern ? 包装图案? :-). :-)。

Custom javax.activation.DataSource implementation (only 4 methods) to be able to do this ? 自定义javax.activation.DataSource实现(只有4种方法)能够做到这一点吗?

return new DataHandler(new DataSource() { 
  // implement getOutputStream to return the stream used inside writeToOut() 
  ... 
});   

I don't have the IDE available to test this so i'm only doing a suggestion. 我没有可用的IDE来测试这个,所以我只是在做一个建议。 I would also need the writeToOut general layout :-). 我还需要writeToOut总体布局:-)。

In my application I use InputStreamDataSource implementation that take InputStream as constructor argument instead of File in FileDataSource. 在我的应用程序中,我使用InputStreamDataSource实现,它将InputStream作为构造函数参数而不是FileDataSource中的File。 It works so far. 它到目前为止工作。

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");
}

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM