Java InputStream의 내용을 OutputStream에 작성하는 쉬운 방법


445

나는 내가의 내용을 쓸 수있는 간단한 방법을 추적 할 수 없다는 것을 오늘 발견 놀랐습니다 InputStreamOutputStream자바를. 분명히 바이트 버퍼 코드를 작성하는 것은 어렵지 않지만 인생을 더 쉽게 (그리고 코드를 더 명확하게) 만들 수있는 것이 빠져 있다고 생각합니다.

따라서 InputStream inand가 주어지면 OutputStream 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에서는 한 줄의 코드로 수행 할 수 있음).
Jabari 2016 년

답변:


182

자바 9

Java 9부터 다음 서명으로 InputStream호출되는 메소드를 제공합니다 transferTo.

public long transferTo(OutputStream out) throws IOException

현상태대로 문서의 상태, transferTo것입니다 :

이 입력 스트림로부터 모든 바이트를 읽고, 읽은 순서대로 지정된 출력 스트림에 바이트를 기입합니다. 돌아 왔을 때,이 입력 스트림은 스트림의 말미에 있습니다. 이 방법은 스트림을 닫지 않습니다.

이 방법은 입력 스트림에서 무한히 읽거나 출력 스트림에 쓰는 것을 차단할 수 있습니다. 입력 및 / 또는 출력 스트림이 비동기식으로 닫히거나 전송 중에 스레드가 중단 된 경우의 동작은 입력 및 출력 스트림에 따라 달라 지므로 지정되지 않습니다.

그래서 자바의 내용을 쓰기 위해 InputStream에를 OutputStream, 당신은 쓸 수 있습니다 :

input.transferTo(output);

11
Files.copy가능한 많이 선호해야 합니다. 네이티브 코드로 구현되므로 더 빠를 수 있습니다. transferTo두 스트림이 모두 FileInputStream / FileOutputStream이 아닌 경우에만 사용해야합니다.
ZhekaKozlov

@ZhekaKozlov 불행하게도 모든 입력 / 출력 스트림을 Files.copy처리하지 않지만 파일 스트림을 위해 특별히 설계되었습니다 .
팔러

396

WMR에서 언급했듯이 org.apache.commons.io.IOUtilsApache에는 copy(InputStream,OutputStream)원하는 것을 정확하게 수행 하는 메소드 가 있습니다.

따라서, 당신은 :

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

... 당신의 코드에서.

피해야 할 이유가 IOUtils있습니까?


170
나는이 모바일 앱을 피하고 있는데, 5 줄의 코드를 저장하기 위해 앱의 크기를 5 배로 만들었습니다.
Jeremy Logan

36
그것은 그 언급 어쩌면 가치 inout최종적으로 블록의 코드의 끝 부분에 닫아야합니다
basZero

24
@basZero 또는 try with resources 블록을 사용하십시오.
Warren Dew

1
(에 ... 소요에 적은 시간에) 또는 당신은 단지 래퍼 ... (아웃) 자신의 사본을 작성할 수 있습니다
MikeM

1
이미 Guava 라이브러리를 사용하고 있다면 Andrejs는 아래의 ByteStreams 클래스를 권장합니다. IOUtils의 기능과 유사하지만 프로젝트에 Commons IO를 추가하지 않습니다.
Jim Tough

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(그렇지 않으면 FileAlreadyExistsExceptionthrow 됨).

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

26
한쪽 끝이 경로이기 때문에 이것이 실제로 문제를 해결한다고 생각하지 않습니다. 파일의 경로를 얻을 수는 있지만 일반적인 스트림 (예 : 네트워크를 통한 스트림)을 얻을 수 없다는 것을 알고 있습니다.
매트 셰퍼드

4
CopyOptions는 임의적입니다! 원한다면 여기에 둘 수 있습니다.
user1079877

4
지금 이것은 내가 찾던 것입니다! 구조에 JDK, 다른 라이브러리에 대한 필요가 없습니다
돈 치들

7
참고 FilesAndroid 의 Java 1.7 에서는 사용할 수 없습니다 . 나는 이것에 찔 렸다 : stackoverflow.com/questions/24869323/…
Joshua Pinter

23
흥미롭게도 JDK Files.copy()에는 두 개의 스트림 Files.copy()이 있으며 실제 복사 작업을 수행하기 위해 다른 모든 기능이 앞으로 제공됩니다. 그러나 그것은 비공개입니다 (실제로 그 단계에서 경로 또는 파일을 포함하지 않기 때문에) . OP 자체 질문의 코드 와 정확히 같습니다 (반환 진술). 오프닝, 클로징, 카피 루프가 없습니다.
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

6
-method를 사용할 때 후자가 0을 반환 할 수 있기 때문에 후자 while(len > 0)대신 말할 수도 있습니다 . 예외를 발생시키는 @!= -1read(byte b[], int off, int len)out.write
phil294

12
@Blauhirn : InputStream읽기에 대한 계약 에 따라 0을 여러 번 반환 하는 것이 전적으로 합법적이므로 잘못된 것 입니다. 그리고에 따라 OutputStream계약, 쓰기 방법은 0의 길이를 수용해야하며, 경우에만 예외를 발생한다 len부정적이다.
Christoffer Hammarström

1
while를 a 로 변경하고 forfor 's init 섹션에 변수 중 하나를 넣어 줄을 저장할 수 있습니다 ( 예 :) for (int n ; (n = in.read(buf)) != -1 ;) out.write(buf, 0, n);. =)
ɲ euroburɳ

1
@Blauhim read()은 길이가 0 인 경우에만 0을 반환 할 수 있는데, 이는 프로그래밍 오류가 될 수 있으며 바보 같은 조건이 영원히 반복됩니다. 그리고 write()않습니다 하지 당신이 제로의 길이를 제공하는 경우 예외를 throw합니다.
Lorne의 후작

54

구아바 사용 ByteStreams.copy():

ByteStreams.copy(inputStream, outputStream);

11
그 후에 스트림을 닫는 것을 잊지 마십시오!
WonderCsabo

이것은 구아바를 이미 사용하고 있다면 나에게 없어서는 안될 최고의 답변입니다.
Hong

1
@ 홍 당신은 Files.copy가능한 많이 사용해야 합니다. ByteStreams.copy두 스트림이 모두 FileInputStream / FileOutputStream이 아닌 경우에만 사용하십시오 .
ZhekaKozlov

@ZhekaKozlov 팁 주셔서 감사합니다. 필자의 경우 입력 스트림은 Android 앱의 리소스 (드로어 블)에서 온 것입니다.
Hong

26

간단한 기능

당신이 단지를 작성이 필요하면 InputStreamA와 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

@JoshPinter 아프지 않을 것입니다.
Jordan LaPrise

3
실제 구현에서 finally 블록을 포함하고 예외를 삼키지 않아야합니다. 또한 메소드에 전달 된 InputStream을 닫는 것은 호출 메소드에 의해 예기치 않은 경우가 있기 때문에 원하는 동작인지 고려해야합니다.
Cel Skeggs

2
IOException이 충분할 때 왜 예외가 발생합니까?
Prabhakar

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

17

PipedInputStream그리고 PipedOutputStream여러 스레드가있는 경우로서 만 사용되어야 Javadoc을 지적한 .

또한 입력 스트림과 출력 스트림은 스레드 중단을 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가 이미 언급 했듯이이 아이디어를 가진 유일한 사람은 아닙니다 .Jakarta Commons 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
루프 내부의 플러싱은 역효과를냅니다.
Lorne의 후작

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

IMHO보다 작은 스 니펫 (길이 변수의 범위가 좁아짐) :

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); }
Brian de 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이기 때문에 시스템 호출 비용을 상쇄하는 것이 아닙니다.
Lorne의 후작 08.

1

코드에서 버퍼링 의미를 사용 BufferedInputStream하고 BufferedOutputStream제거합니다.

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

왜 '버퍼링 시맨틱을 코드에서 제거'하는 것이 좋은 생각입니까?
Lorne의 후작

2
버퍼링 로직을 직접 쓰지 않고 JDK에 내장 된 로직을 사용하는 것이 좋습니다.
Archimedes Trajano

0

PipedInputStream 및 PipedOutputStream은 서로 연결할 수 있으므로 일부 사용 중일 수 있습니다.


1
교착 상태가 발생할 수 있으므로 단일 스레드 코드에는 좋지 않습니다. 이 질문을보십시오 stackoverflow.com/questions/484119/…
Raekye

2
어떻게 사용할 수 있습니까? 그는 이미 입력 스트림과 출력 스트림을 가지고 있습니다. 각각 하나를 추가하면 정확히 어떻게 도움이 되나요?
Lorne의 후작 08.

0

가능한 다른 후보는 Guava I / O 유틸리티입니다.

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

구아바가 하나의 함수에 다른 라이브러리를 추가하는 대신 이미 프로젝트에서 상당히 유용하기 때문에 이것을 사용할 것이라고 생각했습니다.


있다 copytoByteArray메소드 docs.guava-libraries.googlecode.com/git-history/release/javadoc/...은 (구아바 입력을 호출 / 출력 "문자 스트림"로 "바이트 스트림"독자 / 작가로 스트림)
Raekye

구아바 라이브러리를 이미 사용하고 있다면 좋은 아이디어이지만 그렇지 않은 경우 'google-way-of-doing-everything-different-to-the-standard'방법으로 수천 가지의 거대한 라이브러리입니다. 나는 그들에게서 멀리 떨어져 있습니다
rupps

"매머드"? 2.7MB의 매우 작은 종속성과 핵심 JDK의 복제를 신중하게 피하는 API
Adrian Baker

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-loop입니다. 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


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