Maneira fácil para escrever o conteúdo de um Java InputStream para um OutputStream

StackOverflow https://stackoverflow.com/questions/43157

  •  09-06-2019
  •  | 
  •  

Pergunta

Fiquei surpreso ao encontrar, hoje, que eu não conseguia rastrear qualquer maneira simples de escrever o conteúdo de uma InputStream para um OutputStream em Java.Obviamente, o buffer de bytes de código não é difícil de escrever, mas eu suspeito que eu só estou faltando alguma coisa, que iria facilitar minha vida (e o código mais claro).

Assim, dado um InputStream in e um OutputStream out, há uma forma 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);
}
Foi útil?

Solução

Se você estiver usando o Java 7, Arquivos (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)

Editar:É claro que é apenas útil quando você criar um InputStream ou OutputStream do arquivo.Utilização file.toPath() para obter o caminho do arquivo.

Para escrever em um arquivo existente (e.g.uma criada com File.createTempFile()), você vai precisar para passar o REPLACE_EXISTING opção de cópia (caso contrário, FileAlreadyExistsException é lançada):

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

Outras dicas

Como WMR mencionado, org.apache.commons.io.IOUtils a partir do Apache tem um método chamado copy(InputStream,OutputStream) o 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.

Há uma razão que você está evitando IOUtils?

Java 9

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

public long transferTo(OutputStream out) throws IOException

Como o documentação estados, transferTo vai:

Lê todos os bytes a partir deste fluxo de entrada e escreve os bytes para o dado o fluxo de saída na ordem em que eles são lidos.No retorno, este fluxo de entrada vai ser no final do fluxo.Este método não fecha quer transmitir.

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

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

input.transferTo(output);

Eu acho que isso vai funcionar, mas certifique-se de testá-lo...menor "melhoria", mas pode ser um pouco de um custo de legibilidade.

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

Usando Goiaba do ByteStreams.copy():

ByteStreams.copy(inputStream, outputStream);

Função Simples

Se você só precisa isso para escrever um InputStream para um File em seguida, 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();
    }
}

PipedInputStream e PipedOutputStream só deve ser usado quando você tem vários segmentos, como observou o Javadoc.

Além disso, observe que os fluxos de entrada e saída de fluxos de não dispor de qualquer segmento interrupções com IOExceptions...Assim, você deve considerar a incorporação de uma política de interrupção para o 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();
    }
}

Esta seria uma adição útil se você pretende usar essa API para a cópia de grandes volumes de dados, ou dados de fluxos que ficar preso por um intoleravelmente muito tempo.

O JDK utiliza o mesmo código de então parece que não há mais "fáceis" de forma sem desajeitado bibliotecas de terceiros (o que provavelmente não fazem nada de diferente de qualquer jeito).A seguir está diretamente copiados de 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;
    }

Para aqueles que usam Spring framework há um úteis StreamUtils classe:

StreamUtils.copy(in, out);

O disposto acima não se perto de córregos.Se você deseja que os fluxos fechado após a cópia, uso FileCopyUtils em vez de classe:

FileCopyUtils.copy(in, out);

Não há nenhuma maneira de fazer isso muito mais fácil com o JDK métodos, mas como Apocalisp já observado, você não é o único com esta idéia:Você poderia usar IOUtils a partir de Jakarta Commons IO, ele também tem um monte de outras coisas úteis, que IMO, na verdade, deve ser parte do JDK...

Usando o Java7 e tente-com-recursos, vem com um simplificado e legível a versão.

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

Use Commons Net Util classe:

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

Aqui vem como eu estou fazendo com a mais simples para o loop.

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

Um IMHO mais mínima trecho (que também de forma mais restrita escopos de comprimento variável):

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

Como uma nota lateral, eu não entendo por que mais pessoas não usam um for de loop, em vez de optar por um while com um designe-e-teste de expressão que é considerado por alguns como "pobres" de estilo.

Eu acho que é melhor usar um buffer grande, porque a maioria dos arquivos é maior do que 1024 bytes.Também é uma boa prática para verificar o número de bytes de leitura para ser positivo.

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

PipedInputStream e PipedOutputStream pode ser de algum uso, como pode ligar um para o outro.

Outro possível candidato são a Goiaba e/S utilitários:

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

Eu pensei em usar estes desde a Goiabada já é extremamente útil no meu projeto, em vez de adicionar ainda uma outra biblioteca para uma função.

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

try (OutputStream out = new BufferedOutputStream(...);
     InputStream in   = new BufferedInputStream(...))) {
  int ch;
  while ((ch = in.read()) != -1) {
    out.write(ch);
  }
}
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;
}

Tente Cactoos:

new LengthOf(new TeeInput(input, output)).value();

Mais detalhes aqui: http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html

você pode usar esse 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){}
 }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top