Вопрос

У меня есть веб-служба 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.

Есть идеи?

Это было полезно?

Решение

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

Другие советы

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

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

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

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

Образец обертки?:-).

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

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

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

В своем приложении я использую реализацию 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");
}

}

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top