web-dev-qa-db-ja.com

入力ストリームを出力ストリームに接続する

java9の更新: https://docs.Oracle.com/javase/9​​/docs/api/Java/io/InputStream.html#transferTo-Java.io.OutputStream-

似ているが、まったく必要ではないスレッドをいくつか見ました。

私はサーバーを持っています。これは基本的に、クライアントであるクライアントAから入力を受け取り、それをバイトごとに別のクライアントであるクライアントBに転送します。

クライアントAの入力ストリームをクライアントBの出力ストリームに接続したいのですが、可能ですか?それを行う方法は何ですか?

また、これらのクライアントはお互いにメッセージを送信していますが、メッセージは多少時間に依存するため、バッファリングは行いません。たとえば500のバッファは必要ありません。クライアントは499バイトを送信しますが、バッファを満たす最後のバイトを受信して​​いないため、サーバーは500バイトの転送を保留します。

現在、私は各メッセージを解析してその長さを見つけ、次にlengthバイトを読み取ってから転送しています。これは非常に遅いため、バイトを読み取って繰り返し転送するよりも優れていると考えました(そしてテストしました)。また、最後の段落で述べた理由でバッファーやタイマーを使用したくありませんでした。バッファーがいっぱいではないという理由だけで、メッセージが本当に長い間待機するのは望ましくありません。

これを行う良い方法は何ですか?

72
jbu

バッファを使用しているからといって、ストリームがfillそのバッファを必要とするわけではありません。言い換えれば、これは大丈夫です:

public static void copyStream(InputStream input, OutputStream output)
    throws IOException
{
    byte[] buffer = new byte[1024]; // Adjust if you want
    int bytesRead;
    while ((bytesRead = input.read(buffer)) != -1)
    {
        output.write(buffer, 0, bytesRead);
    }
}

基本的にread呼び出しはsomeデータが利用可能になるまでブロックしますが、バッファを満たすためにallが利用可能になるまで待機しません。 (私はそれができると思うし、FileInputStreamは通常willバッファを埋めると思うが、ソケットに接続されたストリームはすぐにデータを提供する可能性が高い。)

少なくともこの単純な解決策を最初に試す価値があると思います。

84
Jon Skeet

使ってみてはいかがですか

void feedInputToOutput(InputStream in, OutputStream out) {
   IOUtils.copy(in, out);
}

そしてそれでやる?

すでに膨大な量のプロジェクトで使用されているジャカルタのApache commons i/oライブラリから、おそらくクラスパスにすでにjarが含まれている可能性があります。

74
Dean Hiller

完全を期すため、 guava には 便利なユーティリティ もあります

ByteStreams.copy(input, output);
21
JesusFreke

JDK 9 が追加されました InputStream#transferTo(OutputStream out) この機能用。

12
Warren M. Nocos

循環バッファを使用できます:

コード

// buffer all data in a circular buffer of infinite size
CircularByteBuffer cbb = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
class1.putDataOnOutputStream(cbb.getOutputStream());
class2.processDataFromInputStream(cbb.getInputStream());


Maven依存関係

<dependency>
    <groupId>org.ostermiller</groupId>
    <artifactId>utils</artifactId>
    <version>1.07.00</version>
</dependency>


モードの詳細

http://ostermiller.org/utils/CircularBuffer.html

9
Stephan

それを達成するための非同期の方法。

void inputStreamToOutputStream(final InputStream inputStream, final OutputStream out) {
    Thread t = new Thread(new Runnable() {

        public void run() {
            try {
                int d;
                while ((d = inputStream.read()) != -1) {
                    out.write(d);
                }
            } catch (IOException ex) {
                //TODO make a callback on exception.
            }
        }
    });
    t.setDaemon(true);
    t.start();
}
6
Daniel De León

BUFFER_SIZEは読み込むチャックのサイズです。1kb以上10 MB未満である必要があります。

private static final int BUFFER_SIZE = 2 * 1024 * 1024;
private void copy(InputStream input, OutputStream output) throws IOException {
    try {
        byte[] buffer = new byte[BUFFER_SIZE];
        int bytesRead = input.read(buffer);
        while (bytesRead != -1) {
            output.write(buffer, 0, bytesRead);
            bytesRead = input.read(buffer);
        }
    //If needed, close streams.
    } finally {
        input.close();
        output.close();
    }
}
2
Leandro Latorre

Org.Apache.commons.io.IOUtilsを使用します

InputStream inStream = new ...
OutputStream outStream = new ...
IOUtils.copy(inStream, outStream);

またはcopyLargeサイズが2GBを超える場合

1
Adam111p

これはScalaクリーンで高速なバージョンです(スタックオーバーフローなし):

  import scala.annotation.tailrec
  import Java.io._

  implicit class InputStreamOps(in: InputStream) {
    def >(out: OutputStream): Unit = pipeTo(out)

    def pipeTo(out: OutputStream, bufferSize: Int = 1<<10): Unit = pipeTo(out, Array.ofDim[Byte](bufferSize))

    @tailrec final def pipeTo(out: OutputStream, buffer: Array[Byte]): Unit = in.read(buffer) match {
      case n if n > 0 =>
        out.write(buffer, 0, n)
        pipeTo(out, buffer)
      case _ =>
        in.close()
        out.close()
    }
  }

これにより、>シンボル例inputstream > outputstreamまた、カスタムバッファ/サイズも渡します。

0
pathikrit