Java InputStreamのコンテンツをOutputStreamに書き込む簡単な方法


445

私は私が内容を書くための任意の簡単な方法突き止めることができなかったことを今日見つけて驚いたInputStreamOutputStreamJavaでを。明らかに、バイトバッファコードを書くのは難しくありませんが、私は自分の人生を楽にする(そしてコードをより明確にする)何かが欠けているだけだと思います。

では、とを指定するInputStream inOutputStream out、次のように書く簡単な方法はありますか?

byte[] buffer = new byte[1024];
int len = in.read(buffer);
while (len != -1) {
    out.write(buffer, 0, len);
    len = in.read(buffer);
}

これはモバイルアプリ用であるとコメントで述べました。ネイティブのAndroidですか?もしそうなら、私に知らせて、私は別の答えを投稿します(それができるのは、Androidの1行のコードです)。
Jabari、2016年

回答:


182

Java 9

Java 9以降、次のシグネチャでInputStream呼び出されるメソッドが提供されtransferToています。

public long transferTo(OutputStream out) throws IOException

などのドキュメントの状態、transferTo以下となります。

この入力ストリームからすべてのバイトを読み取り、読み取られた順序でバイトを指定された出力ストリームに書き込みます。戻ったとき、この入力ストリームはストリームの最後になります。このメソッドはどちらのストリームも閉じません。

このメソッドは、入力ストリームからの読み取り、または出力ストリームへの書き込みを無期限にブロックする場合があります。入力ストリームまたは出力ストリーム、あるいはその両方が非同期に閉じられた場合、または転送中にスレッドが中断された場合の動作は、入力ストリームと出力ストリームに固有であるため、指定されていません。

だから、ジャワの内容を書くためにInputStreamOutputStream、あなたが書くことができます。

input.transferTo(output);

11
あなたはFiles.copyできるだけ多くを好むべきです。ネイティブコードで実装されているため、より高速にできます。transferTo両方のストリームがFileInputStream / FileOutputStreamではない場合にのみ使用してください。
ZhekaKozlov

@ZhekaKozlovは残念ながらFiles.copy対応していない任意の入力/出力ストリームを、それを具体的にするために設計されていたファイルストリーム。
インパラー

396

WMRが述べたようにorg.apache.commons.io.IOUtils、Apacheには、copy(InputStream,OutputStream)まさにあなたが探しているものを実行するというメソッドがあります。

だから、あなたは持っています:

InputStream in;
OutputStream out;
IOUtils.copy(in,out);
in.close();
out.close();

...あなたのコードで。

あなたが避けている理由はありますIOUtilsか?


170
私が作成しているこのモバイルアプリでは、アプリのサイズを5倍にしてコードをわずか5行節約できるので、これを避けています。
ジェレミーローガン

36
それはそれを言及するかもしれない価値があるinoutfinallyブロック内のコードの最後に閉じなければならない
basZero

24
@basZeroまたはtry with resourcesブロックを使用します。
ウォーレンデュー2014年

1
または、独自のコピー(イン、アウト)ラッパーを作成することもできます...(所要時間を
短縮

1
すでにGuavaライブラリを使用している場合、Andrejsは以下のByteStreamsクラスを推奨しています。IOUtilsと同じですが、プロジェクトにCommons IOを追加しません。
ジムタフ14

328

Java 7を使用している場合は、(標準ライブラリの)ファイルが最善の方法です。

/* You can get Path from file also: file.toPath() */
Files.copy(InputStream in, Path target)
Files.copy(Path source, OutputStream out)

編集:もちろん、ファイルからInputStreamまたはOutputStreamのいずれかを作成するときに便利です。file.toPath()ファイルからパスを取得するために使用します。

既存のファイル(たとえばで作成されたファイル)に書き込むにはFile.createTempFile()REPLACE_EXISTINGコピーオプションを渡す必要があります(そうでない場合FileAlreadyExistsExceptionはスローされます)。

Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING)

26
一端がパスであるため、これで実際に問題が解決するとは思わない。ファイルのパスを取得することはできますが、私が認識している限り、一般的なストリーム(たとえば、ネットワーク上のストリーム)のパスは取得できません。
Matt Sheppard、2013年

4
CopyOptionsは任意です!必要に応じて、ここに置くことができます。
user1079877 14

4
これが私が探していたものです!JDKが救い出し、別のライブラリは不要
Don Cheadle 14

7
参考までにFilesAndroidのJava 1.7では使用できません。:私はこれに刺されてしまったstackoverflow.com/questions/24869323/...
ジョシュア・ピンター

23
面白いことに、JDK Files.copy()には2つのストリームを取得するaもあり、Files.copy()コピーの実際の作業を行うために、他のすべての関数が先に進んでいます。ただし、それはプライベートであり(その段階では実際にはパスまたはファイルを含まないため)、OP自体の質問のコード(およびreturnステートメント)とまったく同じように見えます。オープンなし、クローズなし、コピーループのみ。
Ti Strga

102

これはうまくいくと思いますが、必ずテストしてください...マイナーな「改善」ですが、読みやすさの面で少しコストがかかる可能性があります。

byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
    out.write(buffer, 0, len);
}

26
少なくとも10KBから100KBのバッファーをお勧めします。それはそれほど多くなく、大量のデータのコピーを非常に高速化できます。
Aaron Digulla 2008

6
あなたが言いたいのかもしれないwhile(len > 0)代わりに!= -1使用した場合、後者はまた、0を返す可能性があるため、read(byte b[], int off, int len)例外をスロー-methodを、@out.write
phil294

12
@Blauhirn:InputStreamread の契約によると、何度でも0を返すことは完全に合法であるため、これは誤りです。また、OutputStream契約によると、書き込みメソッドは長さ0を受け入れる必要があり、lenが負の場合にのみ例外をスローする必要があります。
ChristofferHammarström15年

1
whileをaに変更しfor、変数の1つをforのinitセクションに置くことで、行を保存できますfor (int n ; (n = in.read(buf)) != -1 ;) out.write(buf, 0, n);。=)
ɲeuroburɳ2015

1
@Blauhim read()がゼロを返すことができるのは、ゼロの長さを指定した場合のみです。これはプログラミングエラーであり、永久にループする愚かな条件です。また、長さがゼロの場合、例外スローされwrite()ませ
ローンの侯爵

54

Guavaの使用ByteStreams.copy()

ByteStreams.copy(inputStream, outputStream);

11
その後、ストリームを閉じることを忘れないでください!
WonderCsabo 2014年

これは、すでに私にとって欠かせないグアバを使用している場合の最良の答えです。
香港

1
@HongあなたはFiles.copy可能な限り使用する必要があります。ByteStreams.copy両方のストリームがFileInputStream / FileOutputStreamではない場合にのみ使用します。
ZhekaKozlov

@ZhekaKozlov先端をありがとう。私の場合、入力ストリームはAndroidアプリのリソース(描画可能)からのものです。
Hong

26

シンプルな機能

これInputStreamをaに書き込むためだけに必要Fileな場合は、この単純な関数を使用できます。

private void copyInputStreamToFile( InputStream in, File file ) {
    try {
        OutputStream out = new FileOutputStream(file);
        byte[] buf = new byte[1024];
        int len;
        while((len=in.read(buf))>0){
            out.write(buf,0,len);
        }
        out.close();
        in.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

4
素晴らしい機能、ありがとう。ただし、close()呼び出しをfinallyブロックに入れる必要がありますか?
Joshua Pinter 2015年

@JoshPinter痛くないです。
ジョーダンラプリーズ、2015年

3
おそらく、実際の実装では、finallyブロックを含め、例外を飲み込まないでください。また、メソッドに渡されたInputStreamを閉じることは、呼び出し側のメソッドでは予期しない場合があるため、それが目的の動作かどうかを検討する必要があります。
Cel Skeggs、

2
IOExceptionで十分なのに、なぜ例外をキャッチするのですか?
Prabhakar 2017年

18

JDK(おそらく何も異なるとにかくをしない)不格好なサードパーティのライブラリのないところに「簡単に」方法がないようにそれはそうして同じコードを使用しています。以下はから直接コピーされjava.nio.file.Files.javaます:

// buffer size used for reading and writing
private static final int BUFFER_SIZE = 8192;

/**
  * Reads all bytes from an input stream and writes them to an output stream.
  */
private static long copy(InputStream source, OutputStream sink) throws IOException {
    long nread = 0L;
    byte[] buf = new byte[BUFFER_SIZE];
    int n;
    while ((n = source.read(buf)) > 0) {
        sink.write(buf, 0, n);
        nread += n;
    }
    return nread;
}

2
はい。この特定の呼び出しはプライベートであり、ファイルではなく2つのソケットを同時に処理する可能性があるため、独自のユーティリティクラスにコピーする以外に選択肢はありません。
Dragas 2018

17

PipedInputStreamまた、Javadocに記載されてPipedOutputStreamいるように、複数のスレッドがある場合にのみ使用してください。

また、入力ストリームと出力ストリームはスレッド割り込みをIOExceptionsでラップしないことに注意してください。したがって、コードに割り込みポリシーを組み込むことを検討する必要があります。

byte[] buffer = new byte[1024];
int len = in.read(buffer);
while (len != -1) {
    out.write(buffer, 0, len);
    len = in.read(buffer);
    if (Thread.interrupted()) {
        throw new InterruptedException();
    }
}

これは、このAPIを使用して大量のデータをコピーしたり、許容できないほど長い時間スタックしたストリームからデータをコピーしたりする場合に役立ちます。


14

Springフレームワークを使用する人のために、便利なStreamUtilsクラスがあります。

StreamUtils.copy(in, out);

上記はストリームを閉じません。コピー後にストリームを閉じたい場合は、代わりにFileCopyUtilsクラスを使用します。

FileCopyUtils.copy(in, out);

8

JDKメソッドを使用してこれを簡単に行う方法はありませんが、Apocalispがすでに述べたように、このアイデアを持つのはあなただけではありません。JakartaCommons IOのIOUtilsを使用することもできます。 IMOは実際にはJDKの一部である必要があります...


6

Java7とtry-with-resourcesを使用すると、シンプルで読みやすいバージョンが付属しています。

try(InputStream inputStream = new FileInputStream("C:\\mov.mp4");
    OutputStream outputStream = new FileOutputStream("D:\\mov.mp4")) {

    byte[] buffer = new byte[10*1024];

    for (int length; (length = inputStream.read(buffer)) != -1; ) {
        outputStream.write(buffer, 0, length);
    }
} catch (FileNotFoundException exception) {
    exception.printStackTrace();
} catch (IOException ioException) {
    ioException.printStackTrace();
}

3
ループ内部のフラッシングは非常に非生産的です。
ローンの侯爵

5

これが、最も単純なforループを使用して実行する方法です。

private void copy(final InputStream in, final OutputStream out)
    throws IOException {
    final byte[] b = new byte[8192];
    for (int r; (r = in.read(b)) != -1;) {
        out.write(b, 0, r);
    }
}

4

Commons NetのUtilクラスを使用します。

import org.apache.commons.net.io.Util;
...
Util.copyStream(in, out);

3

私見のより最小限のスニペット(長さ変数のスコープもより狭くなります):

byte[] buffer = new byte[2048];
for (int n = in.read(buffer); n >= 0; n = in.read(buffer))
    out.write(buffer, 0, n);

for余談ですが、なぜ多くの人がループを使用しないのか理解できません。代わりにwhile、「貧しい」スタイルと見なされる割り当てとテストの式を使用することを選択します。


1
提案により、最初の反復で0バイトの書き込みが発生します。おそらく、少なくとも実行しますfor(int n = 0; (n = in.read(buffer)) > 0;) { out.write(buffer, 0, n); }
ブライアン・デ・Alwis

2
@BriandeAlwis最初の反復が正しくないのはあなたの言うとおりです。コードが修正されました(提案よりもクリーンな方法でIMHO)-編集されたコードを参照してください。思いやりのあるTHX。
ボヘミアン

3

これが私のベストショットです!

また、inputStream.transferTo(...)一般的すぎるため使用しないでください。 バッファメモリを制御すると、コードのパフォーマンスが向上します。

public static void transfer(InputStream in, OutputStream out, int buffer) throws IOException {
    byte[] read = new byte[buffer]; // Your buffer size.
    while (0 < (buffer = in.read(read)))
        out.write(read, 0, buffer);
}

事前にストリームのサイズがわかっている場合は、この(改善可能な)メソッドで使用します。

public static void transfer(int size, InputStream in, OutputStream out) throws IOException {
    transfer(in, out,
            size > 0xFFFF ? 0xFFFF // 16bits 65,536
                    : size > 0xFFF ? 0xFFF// 12bits 4096
                            : size < 0xFF ? 0xFF // 8bits 256
                                    : size
    );
}

2

ほとんどのファイルは1024バイトを超えるので、大きなバッファーを使用する方が良いと思います。また、読み取りバイト数が正であることを確認することをお勧めします。

byte[] buffer = new byte[4096];
int n;
while ((n = in.read(buffer)) > 0) {
    out.write(buffer, 0, n);
}
out.close();

4
大きなバッファーを使用することは確かに良い考えですが、ファイルの大部分が1kより大きいため、システムコールのコストを償却するためではありません。
ローンの侯爵2013年

1

とを使用BufferedInputStreamBufferedOutputStreamて、コードからバッファリングセマンティクスを削除します

try (OutputStream out = new BufferedOutputStream(...);
     InputStream in   = new BufferedInputStream(...))) {
  int ch;
  while ((ch = in.read()) != -1) {
    out.write(ch);
  }
}

なぜ「コードからバッファリングセマンティクスを削除する」のが良い考えなのですか?
ローン侯爵

2
これは、私がバッファリングロジックを自分で作成するのではなく、JDKに組み込まれているロジックを使用することを意味します。
アルキメデストラハノ

0

PipedInputStreamとPipedOutputStreamは、一方を他方に接続できるため、役立つ場合があります。


1
これはデッドロックを引き起こす可能性があるため、シングルスレッドコードには適していません。この質問を見stackoverflow.com/questions/484119/...
Raekye

2
なんとなく役に立つかもしれませんか?彼はすでに入力ストリームと出力ストリームを持っています。各ヘルプをもう1つ追加するとどのように正確に役立ちますか?
ローンの侯爵2013年

0

別の候補として、Guava I / Oユーティリティがあります。

http://code.google.com/p/guava-libraries/wiki/IOExplained

Guavaは自分のプロジェクトですでに非常に便利なので、1つの関数にさらに別のライブラリを追加するのではなく、これらを使用すると思いました。


あるcopytoByteArrayのメソッドdocs.guava-libraries.googlecode.com/git-history/release/javadoc/...に(「バイトストリーム」と「文字ストリーム」として、リーダ/ライタとしてグアバ呼び出し入力/出力ストリーム)
Raekye

既にグアバライブラリを使用している場合はそれをお勧めしますが、使用していない場合は、何千ものメソッド「google-way-of-doing-everything-different-to-the-standard」を備えた巨大なライブラリです。私はそれらから遠ざけます
2014年

"マンモス"?依存関係の非常に小さなセットを備えた2.7MB、およびコアJDKの複製を慎重に回避するAPI。
エイドリアンベイカー

0

あまり読みやすくはありませんが、効果的で、依存関係がなく、どのJavaバージョンでも実行できます

byte[] buffer = new byte[1024];
for (int n; (n = inputStream.read(buffer)) != -1; outputStream.write(buffer, 0, n));

!= -1または> 0?これらの述語はまったく同じではありません。
インパラー

!= -1は、ファイルの終わりでないことを意味します。これは反復ではなく、変装したwhile-doループです:while((n = inputStream.read(buffer))!= -1)do {outputStream.write(buffer、0、n)}
IPP Nerd

-1
public static boolean copyFile(InputStream inputStream, OutputStream out) {
    byte buf[] = new byte[1024];
    int len;
    long startTime=System.currentTimeMillis();

    try {
        while ((len = inputStream.read(buf)) != -1) {
            out.write(buf, 0, len);
        }

        long endTime=System.currentTimeMillis()-startTime;
        Log.v("","Time taken to transfer all bytes is : "+endTime);
        out.close();
        inputStream.close();

    } catch (IOException e) {

        return false;
    }
    return true;
}

4
これが正しい答えである理由を説明していただけますか?
rfornal 2015


-6

あなたはこの方法を使うことができます

public static void copyStream(InputStream is, OutputStream os)
 {
     final int buffer_size=1024;
     try
     {
         byte[] bytes=new byte[buffer_size];
         for(;;)
         {
           int count=is.read(bytes, 0, buffer_size);
           if(count==-1)
               break;
           os.write(bytes, 0, count);
         }
     }
     catch(Exception ex){}
 }

6
catch(Exception ex){}—これは最高です
ᄂ ᄀ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.