Domanda

Ho un servizio Web Java JAX-WS che restituisce un OutputStream da un altro metodo. Io non riesco a capire come per lo streaming OutputStream nella DataHandler tornato altro modo se non per creare un file temporaneo, scrivere su di esso, quindi aprirlo di nuovo di nuovo come un InputStream. Ecco un esempio:

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

Il problema principale è che il metodo writeToOut () può restituire i dati che sono molto più grandi di memoria del computer. È per questo che il metodo sta usando MTOM, in primo luogo - per lo streaming dei dati. Non riesco ad avvolgere la mia testa intorno la modalità di streaming i dati direttamente dal OutputStream che ho bisogno di fornire al DataHandler tornato (e in ultima analisi il cliente, che riceve lo StreamingDataHandler).

Ho provato a giocare con PipedInputStream e PipedOutputStream, ma quelli non sembrano essere abbastanza quello che mi serve, perché il DataHandler avrebbe bisogno di essere restituito dopo il PipedOutputStream viene scritto.

Tutte le idee?

È stato utile?

Soluzione

ho capito la risposta, lungo le linee che Christian stava parlando (la creazione di un nuovo thread per eseguire 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;
    }
}

E 'un po' più complessa a causa di final variabili, ma per quanto posso dire questo è corretto. Quando il filo viene avviato, blocca quando si tenta di chiamare prima out.write(); allo stesso tempo, il flusso di input viene restituito al cliente, che sblocca la scrittura leggendo i dati. (Il problema con i miei precedenti implementazioni di questa soluzione è che non ero corretta chiusura del torrente, e correre così in errori.)

Altri suggerimenti

Siamo spiacenti, ho fatto solo questo per C # e non java, ma penso che il metodo dovrebbe avviare un thread per eseguire "writeToOut (out);" in parralel. È necessario creare un flusso speciale e passarlo al nuovo thread, che dà quel flusso di writeToOut. Dopo aver avviato il filo ritorni che stream-oggetto per il chiamante.

Se avete solo un metodo che scrive in un flusso e restituisce dopo e un altro metodo che consuma un flusso e restituisce dopo, non c'è altro modo.

coure la parte difficile è procurarsi un flusso safe tale -multithreading:. Esso bloccare ogni lato se un buffer interno è troppo pieno

Non so se un Java-pipe-stream lavora per questo.

modello Wrapper? : -).

implementazione personalizzata javax.activation.DataSource (solo 4 metodi) per essere in grado di fare questo?

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

Non ho l'IDE a disposizione per testare questo modo che io faccio solo un suggerimento. Vorrei anche bisogno del writeToOut layout generale :-).

Nella mia applicazione uso implementazione InputStreamDataSource che prendono InputStream come argomento del costruttore, invece di file in FileDataSource. Funziona così lontano.

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

}

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top