java - usar - write inputstream to fileoutputstream




Maneira fácil de gravar o conteúdo de um Java InputStream em um OutputStream (14)

Java 9

Desde o Java 9, o InputStream fornece um método chamado transferTo com a seguinte assinatura:

public long transferTo(OutputStream out) throws IOException

Como a documentation afirma, transferTo irá:

Lê todos os bytes desse fluxo de entrada e grava os bytes no fluxo de saída determinado na ordem em que são lidos. No retorno, esse fluxo de entrada estará no final do fluxo. Este método não fecha nenhum fluxo.

Esse método pode bloquear indefinidamente a leitura do fluxo de entrada ou gravar no fluxo de saída. O comportamento para o caso em que o fluxo de entrada e / ou saída é fechado de forma assíncrona, ou o encadeamento interrompido durante a transferência, é altamente específico de fluxo de entrada e saída e, portanto, não especificado

Portanto, para gravar o conteúdo de um Java InputStream em um OutputStream , você pode escrever:

input.transferTo(output);

Fiquei surpreso ao descobrir hoje que não consegui rastrear nenhuma maneira simples de escrever o conteúdo de um InputStream em um OutputStream em Java. Obviamente, o código do buffer de byte não é difícil de escrever, mas desconfio que estou perdendo algo que facilitaria minha vida (e o código mais claro).

Então, dado um InputStream in e um OutputStream out , existe uma maneira mais simples de escrever o seguinte?

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

Função simples

Se você só precisa disso para escrever um InputStream para um File então você pode usar esta função simples:

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

Como o WMR mencionou, org.apache.commons.io.IOUtils do Apache tem um método chamado copy(InputStream,OutputStream) que faz exatamente o que você está procurando.

Então você tem:

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

... no seu código.

Existe uma razão pela qual você está evitando IOUtils ?


Eu acho que é melhor usar um buffer grande, porque a maioria dos arquivos são maiores que 1024 bytes. Também é uma boa prática verificar o número de bytes lidos como positivos.

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

Eu uso BufferedInputStream e BufferedOutputStream para remover a semântica de buffer do código

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

Não há como fazer isso muito mais facilmente com os métodos JDK, mas como o Apocalisp já observou, você não é o único com essa idéia: você pode usar o IOUtils do Jakarta Commons IO , ele também tem muitas outras coisas úteis, que a OMI deveria realmente fazer parte do JDK ...



Para aqueles que usam o Spring framework, há uma classe útil StreamUtils :

StreamUtils.copy(in, out);

O acima não fecha os fluxos. Se você deseja que os fluxos sejam fechados após a cópia, use a classe FileCopyUtils :

FileCopyUtils.copy(in, out);

Se você estiver usando o Java 7, o Files (na biblioteca padrão) é a melhor abordagem:

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

Edit: Claro que é apenas útil quando você cria um dos InputStream ou OutputStream do arquivo. Use file.toPath() para obter o caminho do arquivo.

Para escrever em um arquivo existente (por exemplo, um criado com File.createTempFile() ), você precisará passar a opção de cópia REPLACE_EXISTING (caso contrário, FileAlreadyExistsException será lançado):

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


Usando Java7 e try-with-resources , vem com uma versão simplificada e legível.

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


você pode usar este método

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

PipedInputStream e PipedOutputStream só devem ser usados ​​quando você tiver vários encadeamentos, conforme observado pelo Javadoc .

Além disso, observe que os fluxos de entrada e de saída não envolvem nenhuma interrupção de thread com o IOException s ... Portanto, você deve considerar a incorporação de uma política de interrupção ao seu código:

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

Essa seria uma adição útil se você espera usar essa API para copiar grandes volumes de dados ou dados de fluxos que ficam presos por um tempo intoleravelmente longo.





stream