Una forma sencilla de escribir contenidos de un InputStream de Java en un OutputStream

Me sorprendió encontrar hoy que no podía encontrar una forma simple de escribir el contenido de un InputStream en un OutputStream en Java. Obviamente, el código de buffer byte no es difícil de escribir, pero sospecho que me estoy perdiendo algo que haría mi vida más fácil (y el código más claro).

Entonces, dado un InputStream in y un OutputStream out , ¿hay una manera más simple de escribir lo siguiente?

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

Si está utilizando Java 7, Files (en la biblioteca estándar) es el mejor enfoque:

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

Editar: por supuesto, es útil cuando se crea uno de InputStream o OutputStream desde el archivo. Use file.toPath() para obtener la ruta del archivo.

Para escribir en un archivo existente (por ejemplo, uno creado con File.createTempFile() ), deberá pasar la opción de copia REPLACE_EXISTING (de lo contrario, se FileAlreadyExistsException ):

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

Como se mencionó en WMR, org.apache.commons.io.IOUtils de Apache tiene un método llamado copy(InputStream,OutputStream) que hace exactamente lo que estás buscando.

Así que tienes:

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

… en tu código.

¿Hay alguna razón por la que estás evitando IOUtils ?

Creo que esto funcionará, pero asegúrate de probarlo … una “mejora” menor, pero puede ser un poco costoso para la legibilidad.

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

Java 9

Desde Java 9, InputStream proporciona un método llamado transferTo con la siguiente firma:

 public long transferTo(OutputStream out) throws IOException 

Como dice la documentación , transferTo hará lo siguiente:

Lee todos los bytes de esta secuencia de entrada y escribe los bytes en la secuencia de salida dada en el orden en que se leen. A su regreso, esta stream de entrada estará al final de la transmisión. Este método no cierra ninguna secuencia.

Este método puede bloquear la lectura indefinida del flujo de entrada, o escribir en el flujo de salida. El comportamiento para el caso en el que el flujo de entrada y / o salida se cierra de forma asíncrona, o el hilo se interrumpe durante la transferencia, es altamente específico de flujo de entrada y salida y, por lo tanto, no se especifica

Entonces, para escribir los contenidos de un InputStream Java en un OutputStream , puede escribir:

 input.transferTo(output); 

Usando ByteStreams.copy() Guava:

 ByteStreams.copy(inputStream, outputStream); 

Función simple

Si solo necesita esto para escribir un InputStream en un File , puede usar esta sencilla función:

 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 y PipedOutputStream solo deben usarse cuando tiene múltiples hilos, como lo señala el Javadoc .

Además, tenga en cuenta que las secuencias de entrada y de salida no envuelven las interrupciones de subprocesos con IOException s … Por lo tanto, debería considerar incorporar una política de interrupción a su 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(); } } 

Esto sería una adición útil si espera utilizar esta API para copiar grandes volúmenes de datos o datos de transmisiones que se estancan durante un tiempo intolerablemente largo.

No hay forma de hacerlo mucho más fácil con los métodos JDK, pero como Apocalisp ya ha notado, no eres el único con esta idea: podrías usar IOUtils de Yakarta Commons IO , también tiene muchas otras cosas útiles, que la OMI debería ser parte del JDK …

El JDK usa el mismo código, por lo que parece que no hay una forma “más fácil” de utilizar bibliotecas de terceros torpes (que de todos modos no hacen nada diferente). Lo siguiente se copia directamente 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; } 

Usando Java7 y try-with-resources , viene con una versión simplificada y legible.

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

Para aquellos que usan Spring framework hay una clase útil de StreamUtils :

 StreamUtils.copy(in, out); 

Lo anterior no cierra las transmisiones. Si desea que las transmisiones se cierren después de la copia, use en su lugar la clase FileCopyUtils :

 FileCopyUtils.copy(in, out); 

Use la clase Util de Commons Net:

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

Creo que es mejor usar un búfer grande, porque la mayoría de los archivos tienen más de 1024 bytes. También es una buena práctica verificar que la cantidad de bytes leídos sea positiva.

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

Un fragmento más mínimo en mi humilde opinión (que también abarca más estrechamente la variable de longitud):

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

Como nota al margen, no entiendo por qué más personas no usan un bucle for , en cambio optan por un while con una expresión de asignación y prueba que algunos consideran como estilo “pobre”.

Aquí viene lo que estoy haciendo con lo más simple para 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); } } 

PipedInputStream y PipedOutputStream pueden ser de alguna utilidad, ya que puede conectar uno con el otro.

Otro posible candidato son las utilidades de E / S de Guava:

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

Pensé que usaría estos ya que Guava ya es inmensamente útil en mi proyecto, en lugar de agregar otra biblioteca para una función.

Prueba Cactoos :

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

Más detalles aquí: http://www.yegor256.com/2017/06/22/object-oriented-input-output-in-cactoos.html

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

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