OutputStream'den InputStream oluşturmanın en verimli yolu


86

Bu sayfa: http://blog.ostermiller.org/convert-java-outputstream-inputstream , OutputStream'den nasıl bir InputStream oluşturulacağını açıklar:

new ByteArrayInputStream(out.toByteArray())

Diğer alternatifler ise PipedStreams'i ve hantal olan yeni konuları kullanmaktır.

Birçok megabaytı bellek bayt dizisinde yeniye kopyalama fikrinden hoşlanmıyorum. Bunu daha verimli yapan bir kütüphane var mı?

DÜZENLE:

Laurence Gonsalves'ın tavsiyesiyle PipedStreams'i denedim ve başa çıkmanın o kadar da zor olmadığı ortaya çıktı. Clojure'daki örnek kod:

(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))

Yanıtlar:


73

Tüm verileri bir kerede bir bellek içi arabelleğe kopyalamak istemiyorsanız, o zaman OutputStream'i (üretici) ve InputStream'i (tüketici) kullanan koda sahip olmanız gerekir. ) ya aynı iş parçacığında dönüşümlü olun ya da iki ayrı iş parçacığında aynı anda çalışın. Bunların aynı iş parçacığı içinde çalıştırılması, muhtemelen iki ayrı iş parçacığı kullanmaktan çok daha karmaşıktır, çok daha fazla hataya meyillidir (tüketicinin girdi beklemeyi asla engellemediğinden emin olmanız gerekir , aksi takdirde etkin bir şekilde kilitleneceksiniz) üretici ve tüketicinin aynı döngüde çalışması çok sıkı bir şekilde bağlanmış gibi görünüyor.

Öyleyse ikinci bir iplik kullanın. Gerçekten o kadar karmaşık değil. Bağlandığınız sayfanın mükemmel bir örneği vardı:

  PipedInputStream in = new PipedInputStream();
  PipedOutputStream out = new PipedOutputStream(in);
  new Thread(
    new Runnable(){
      public void run(){
        class1.putDataOnOutputStream(out);
      }
    }
  ).start();
  class2.processDataFromInputStream(in);

Ayrıca her bir tüketici iş parçacığı için yeni PipedInputStream oluşturmanız gerektiğini düşünüyorum. Pipe'tan başka bir başlıktan okursanız size bir hata verecektir.
Denis Tulskiy

@Lawrence: 2 iş parçacığı kullanmak için mantığınızı anlamıyorum ... InputStream'den okunan tüm karakterlerin zamanında OutputStream'e yazılması gerekmedikçe.
Stephen C

8
Stephen: Bir şeyi yazmadan okuyamazsınız. Bu nedenle, tek bir iş parçacığı ile ya önce her şeyi yazmanız (Vagif'in kaçınmak istediği büyük bir bellek içi dizi oluşturarak) ya da bunları değiştirerek okuyucunun girdi beklemeyi asla engellememesi için çok dikkatli olmanız gerekir (çünkü yaparsa yazar da asla yürütemez).
Laurence Gonsalves

1
Bu öneri, kapsayıcının muhtemelen kendi iş parçacığının çoğunu çalıştırdığı bir JEE ortamında kullanmak güvenli midir?
Toskan

2
@Toskan new Threadherhangi bir nedenle konteynırınızda uygun değilse, kullanabileceğiniz bir iş parçacığı havuzu olup olmadığına bakın.
Laurence Gonsalves

14

Borular ve dişlerle şeffaf bir şekilde ilgilenen EasyStream adında başka bir Açık Kaynak kitaplığı vardır . Her şey yolunda giderse bu gerçekten karmaşık değil. Sorunlar ortaya çıkıyor (Laurence Gonsalves örneğine bakıldığında)

class1.putDataOnOutputStream (çıkış);

Bir istisna atar. Bu örnekte, iş parçacığı basitçe tamamlanır ve istisna kaybolurken, dış kısım InputStreamkesilebilir.

Easystream, istisna yayılımı ve yaklaşık bir yıldır hata ayıklamakta olduğum diğer kötü sorunlarla ilgilenir. (Ben kütüphanenin yöneticisiyim: açıkçası benim çözümüm en iyisidir;)) İşte nasıl kullanılacağına dair bir örnek:

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

Bir OutputStream'i bir InputStream'e dönüştürmenin diğer tüm yollarının açıklandığı güzel bir giriş de var . Bir göz atmaya değer.


1
Sınıflarını kullanma öğreticisi code.google.com/p/io-tools/wiki/Tutorial_EasyStream
koppor

9

Arabelleği kopyalamaktan kaçınan basit bir çözüm, özel bir amaç oluşturmaktır 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);
    }
}

Gerektiği gibi yukarıdaki çıkış akışına yazın, ardından toInputStreamtemeldeki tampon üzerinden bir giriş akışı elde etmek için arayın . Bu noktadan sonra çıkış akışını kapalı olarak düşünün.


7

InputStream'i bir OutputStream'e bağlamanın en iyi yolunun borulu akışlar olduğunu düşünüyorum - java.io paketinde aşağıdaki gibi mevcuttur:

// 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.*/

Bence bu kodun iki ana avantajı var:

1 - Tampon haricinde ek hafıza tüketimi yoktur.

2 - Veri kuyruğunu manuel olarak işlemenize gerek yoktur


1
Bu harika olurdu, ancak javadoclar , bunları aynı ileti dizisinde okur ve yazarsanız , çıkmaza girebileceğinizi söylüyor. Keşke bunu NIO ile güncellemiş olsalardı!
Nate Glenn

1

Artan kilitlenme olasılığı, kodu anlamanın artan zorluğu ve istisnalarla uğraşmanın sorunları nedeniyle genellikle ayrı bir iş parçacığı oluşturmaktan kaçınmaya çalışıyorum.

İşte benim önerdiğim çözüm: Chunk () üretmek için tekrarlanan çağrılarla parçalar halinde içerik oluşturan bir ProducerInputStream:

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

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.