2009-08-04 13 views
73

このページからInputStreamを作成します。http://blog.ostermiller.org/convert-java-outputstream-inputstream はOutputStreamのからInputStreamを作成する方法について説明します。最も効率的な方法は、OutputStreamの

new ByteArrayInputStream(out.toByteArray()) 

他の選択肢は面倒ですPipedStreams、新たなスレッドを使用することです。

新しいメモリバイト配列に多くのメガバイトをコピーするというのは気に入らないです。 これをより効率的に行うライブラリはありますか?

EDIT:ローレンス・ゴンサルベスからのアドバイスにより、

、私はPipedStreamsを試してみましたが、それは彼らがハードに対処することはありませんが判明しました。ここ は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)) 

答えて

63

あなたはすべてを一度にメモリ内のバッファにデータのすべてをコピーしたくない場合は、使用していますあなたのコードを持っている必要があるとしていますOutputStream(プロデューサ)とInputStream(コンシューマ)を使用するコードは、同じスレッドで交互に動作するか、2つの別々のスレッドで同時に動作します。 2つのスレッドを別々に使用すると、エラーが発生しやすくなります(は入力を待たずにブロックするか、効果的にデッドロックします)。プロデューサーと消費者が同じループで動いていることが必要になります。これはあまりにも緊密に結びついているようです。

したがって、2番目のスレッドを使用してください。それは本当に複雑ではありません。

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

:produceChunk(を繰り返し呼び出しによってチャンクでコンテンツを作成しProducerInputStream):

は、ここに私の提案されたソリューションです。別のスレッドからPipeを読み込むと、エラーが発生します。 –

+0

@Lawrence:2つのスレッドを使用する理由を理解していません... InputStreamから読み込まれたすべての文字がタイムリーにOutputStreamに書き込まれる必要がある場合を除き、 –

+0

Thx。最初はPipedStreamを見落としました。なぜなら、私はそれを扱うのが面倒すぎると思ったからです。大したことは何もなかった。特にクロジャレから。 –

15

透明な方法でパイプやスレッドを扱うEasyStreamと呼ばれる別のオープンソースのライブラリがあります:あなたがにリンクされたページには、完璧な例がありました。 すべてがうまくいくなら、それは本当に複雑ではありません。問題が発生したとき(ローレンス・ゴンサーブの例を見て)

class1.putDataOnOutputStream(out);

例外をスローします。 この例では、スレッドは単に完了し、例外は失われますが、外側のInputStreamは切り捨てられる可能性があります。

私は約1年間デバッグしてきた例外伝搬などの厄介な問題を扱います。 (私は図書館のmantainerだ:明らかに私の解決策は、最良のものです;)) ここでは、それを使用する方法の例です:

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

他のすべての方法は、変換する素敵なintroductionもありInputStreamへのOutputStreamについて説明します。一見する価値がある。

+1

クラスを使用するためのチュートリアルはhttps://code.google.com/p/io-tools/wiki/Tutorial_EasyStream – koppor

+0

で入手できます。それは本当に私の問題を解決します。 – smartwjw

4

InputStreamをOutputStreamに接続する最も良い方法は、パイプストリーム - javaで利用可能です。IOパッケージには、次のように:

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

私の意見では、このコードには2つの主な利点があります。

1 - バッファを除き、メモリの追加の消費はありません。上記の出力ストリームに

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

書き込み: -

2あなたは

+1

これはすばらしいことですが、[javadocs](http://docs.oracle.com/javase/7/docs/api/java/io/PipedInputStream.html)では、同じものを読み書きするとあなたはデッドロックを受ける可能性があります。彼らがNIOでこれを更新したことを祈っています! –

6

手動でバッファをコピーする特別目的ByteArrayOutputStreamを作成することで回避できるシンプルなソリューションをキューイングデータを処理する必要はありません。必要に応じてtoInputStreamを呼び出して、基になるバッファ上の入力ストリームを取得します。その時点の後に出力ストリームが閉じているとします。

0

通常、デッドロックの可能性が増し、コードを理解するのが困難になり、例外を処理する問題が発生するため、別のスレッドを作成しないようにします。私はあなたにも、各消費者のスレッドのための新しい持つPipedInputStreamを作成する必要があると思う

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

}