Как вы можете передать OutputStream к StreamingDataHandler? - PullRequest
8 голосов
/ 15 мая 2009

У меня есть веб-сервис Java в JAX-WS, который возвращает 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, но, похоже, это не совсем то, что мне нужно, потому что DataHandler нужно будет возвращать после записи в PipedOutputStream.

Есть идеи?

Ответы [ 4 ]

4 голосов
/ 16 мая 2009

Я выяснил ответ в соответствии с тем, о чем говорил Кристиан (создав новый поток для выполнения 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(); в то же время входной поток возвращается клиенту, который разблокирует запись, читая данные. (Проблема с моими предыдущими реализациями этого решения заключалась в том, что я не закрывал поток должным образом и, следовательно, сталкивался с ошибками.)

3 голосов
/ 15 мая 2009

Извините, я сделал это только для C #, а не для Java, но я думаю, что ваш метод должен запустить поток для запуска "writeToOut (out);" в парралеле. Вам нужно создать специальный поток и передать его новому потоку, который дает этот поток для writeToOut. После запуска потока вы возвращаете этот потоковый объект вызывающей стороне.

Если у вас есть только метод, который записывает в поток и возвращает потом, а другой метод, который использует поток и возвращает потом, другого пути нет.

Конечно, сложнее всего получить такой многопоточный безопасный поток: он должен блокировать каждую сторону, если внутренний буфер переполнен.

Не знаю, работает ли для этого поток Java-pipe.

1 голос
/ 15 мая 2009

шаблон обертки? : -).

Пользовательская реализация javax.activation.DataSource (только 4 метода), чтобы можно было это сделать?

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

У меня нет среды IDE для тестирования, поэтому я делаю только предложение. Мне также понадобится общий макет writeToOut :-).

0 голосов
/ 11 февраля 2016

В моем приложении я использую реализацию InputStreamDataSource, которая принимает InputStream в качестве аргумента конструктора вместо File в FileDataSource. Это работает до сих пор.

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

}

...