Meest efficiënte manier om InputStream te maken vanuit OutputStream

Deze pagina: http://blog.ostermiller.org/convert-java-outputstream -invoerstroom
beschrijft hoe u een InputStream maakt van OutputStream:

new ByteArrayInputStream(out.toByteArray())

Andere alternatieven zijn het gebruik van PipedStreams en nieuwe threads, wat omslachtig is.

Ik hou niet van het idee om veel megabytes te kopiëren naar een nieuwe byte-array in het geheugen.
Is er een bibliotheek die dit efficiënter doet?

BEWERKEN:

Op advies van Laurence Gonsalves heb ik PipedStreams geprobeerd en het bleek dat ze niet zo moeilijk zijn om mee om te gaan.
Hier is de voorbeeldcode in clojure:

(defn #^PipedInputStream create-pdf-stream [pdf-info]
  (let [in-stream (new PipedInputStream)
        out-stream (PipedOutputStream. in-stream)]
    (.start (Thread. #(;Here you write into out-stream)))
    in-stream))

Antwoord 1, autoriteit 100%

Als u niet alle gegevens in één keer naar een buffer in het geheugen wilt kopiëren, moet u uw code hebben die de OutputStream (de producer) gebruikt en de code die de InputStream gebruikt (de consument) ofwel afwisselend in dezelfde thread, of gelijktijdig werken in twee afzonderlijke threads. Ze in dezelfde thread laten werken is waarschijnlijk veel ingewikkelder dan het gebruik van twee afzonderlijke threads, is veel gevoeliger voor fouten (u moet ervoor zorgen dat de consument nooithet wachten op invoer blokkeert, of u’ in feite een impasse) en zou het nodig maken dat de producent en de consument in dezelfde lus lopen, wat veel te nauw met elkaar lijkt te zijn verbonden.

Gebruik dus een tweede draad. Het is echt niet zo ingewikkeld. De pagina waarnaar u linkte, had een redelijk voorbeeld. Hier is een enigszins gemoderniseerde versie, die ook de streams afsluit:

try (PipedInputStream in = new PipedInputStream()) {
    new Thread(() -> {
        try (PipedOutputStream out = new PipedOutputStream(in)) {
            writeDataToOutputStream(out);
        } catch (IOException iox) {
            // handle IOExceptions
        }
    }).start();
    processDataFromInputStream(in);
}

Antwoord 2, autoriteit 18%

Er is nog een Open Source-bibliotheek genaamd EasyStreamdat op transparante wijze omgaat met buizen en draad.
Dat is niet zo ingewikkeld als alles goed gaat. Er ontstaan ​​problemen wanneer (kijkend naar het voorbeeld van Laurence Gonsalves)

class1.putDataOnOutputStream(uit);

Gooit een uitzondering.
In dat voorbeeld wordt de thread gewoon voltooid en gaat de uitzondering verloren, terwijl de buitenste InputStreammogelijk wordt afgekapt.

Easystream houdt zich bezig met de verspreiding van uitzonderingen en andere vervelende problemen die ik al ongeveer een jaar aan het debuggen ben. (Ik ben de beheerder van de bibliotheek: uiteraard is mijn oplossing de beste 😉 )
Hier is een voorbeeld van hoe het te gebruiken:

final InputStreamFromOutputStream<String> isos = new InputStreamFromOutputStream<String>(){
 @Override
 public String produce(final OutputStream dataSink) throws Exception {
   /*
    * call your application function who produces the data here
    * WARNING: we're in another thread here, so this method shouldn't 
    * write any class field or make assumptions on the state of the outer class. 
    */
   return produceMydata(dataSink)
 }
};

Er is ook een mooie introductiewaar alle andere manieren om een OutputStream in een InputStream worden uitgelegd. De moeite waard om een ​​kijkje te nemen.


Antwoord 3, autoriteit 14%

Een eenvoudige oplossing die het kopiëren van de buffer vermijdt, is het maken van een speciale ByteArrayOutputStream:

public class CopyStream extends ByteArrayOutputStream {
    public CopyStream(int size) { super(size); }
    /**
     * Get an input stream based on the contents of this output stream.
     * Do not use the output stream after calling this method.
     * @return an {@link InputStream}
     */
    public InputStream toInputStream() {
        return new ByteArrayInputStream(this.buf, 0, this.count);
    }
}

Schrijf indien nodig naar de bovenstaande uitvoerstroom en roep vervolgens toInputStreamaan om een ​​invoerstroom via de onderliggende buffer te verkrijgen. Beschouw de uitvoerstroom als gesloten na dat punt.


Antwoord 4, autoriteit 9%

Ik denk dat de beste manier om InputStream te verbinden met een OutputStream is via doorgesluisde streams– beschikbaar in java.io-pakket, als volgt:

// 1- Define stream buffer
private static final int PIPE_BUFFER = 2048;
// 2 -Create PipedInputStream with the buffer
public PipedInputStream inPipe = new PipedInputStream(PIPE_BUFFER);
// 3 -Create PipedOutputStream and bound it to the PipedInputStream object
public PipedOutputStream outPipe = new PipedOutputStream(inPipe);
// 4- PipedOutputStream is an OutputStream, So you can write data to it
// in any way suitable to your data. for example:
while (Condition) {
     outPipe.write(mByte);
}
/*Congratulations:D. Step 4 will write data to the PipedOutputStream
which is bound to the PipedInputStream so after filling the buffer
this data is available in the inPipe Object. Start reading it to
clear the buffer to be filled again by the PipedInputStream object.*/

Naar mijn mening zijn er twee belangrijke voordelen voor deze code:

1 – Er is geen extra geheugengebruik behalve de buffer.

2 – U hoeft gegevenswachtrijen niet handmatig af te handelen


Antwoord 5, autoriteit 3%

Ik probeer meestal geen aparte thread te maken vanwege de grotere kans op een deadlock, de grotere moeilijkheid om de code te begrijpen en de problemen bij het omgaan met uitzonderingen.

Hier is mijn voorgestelde oplossing: een ProducerInputStream die inhoud in brokken creëert door herhaalde aanroepen van produceChunk():

public abstract class ProducerInputStream extends InputStream {
    private ByteArrayInputStream bin = new ByteArrayInputStream(new byte[0]);
    private ByteArrayOutputStream bout = new ByteArrayOutputStream();
    @Override
    public int read() throws IOException {
        int result = bin.read();
        while ((result == -1) && newChunk()) {
            result = bin.read();
        }
        return result;
    }
    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int result = bin.read(b, off, len);
        while ((result == -1) && newChunk()) {
            result = bin.read(b, off, len);
        }
        return result;
    }
    private boolean newChunk() {
        bout.reset();
        produceChunk(bout);
        bin = new ByteArrayInputStream(bout.toByteArray());
        return (bout.size() > 0);
    }
    public abstract void produceChunk(OutputStream out);
}

Other episodes