Manera sencilla de escribir contenidos de un Java InputStream en un OutputStream

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

  •  09-06-2019
  •  | 
  •  

Pregunta

Me sorprendió descubrir hoy que no podía encontrar ninguna forma sencilla de escribir el contenido de un InputStream a una OutputStream en Java.Obviamente, el código del búfer de bytes no es difícil de escribir, pero sospecho que me falta algo que me haría la vida más fácil (y el código más claro).

Entonces, dado un InputStream in y un OutputStream out, ¿Existe una forma más sencilla 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);
}
¿Fue útil?

Solución

Si está utilizando Java 7, Archivos (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 creas uno de InputStream o OutputStream a partir de un archivo.Usar file.toPath() para obtener la ruta del archivo.

Para escribir en un archivo existente (p. ej.uno creado con File.createTempFile()), tendrás que pasar el REPLACE_EXISTING opción de copia (de lo contrario FileAlreadyExistsException es aventado):

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

Otros consejos

Como mencionó 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?

Java 9

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

public long transferTo(OutputStream out) throws IOException

como el documentación estados, transferTo voluntad:

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

Este método puede bloquear la lectura indefinida del flujo de entrada o escribir en la secuencia de salida.El comportamiento para el caso en el que la secuencia de entrada y/o salida se cierra asincrónicamente, o el subproceso interrumpido durante la transferencia es altamente específico de la secuencia de entrada y salida, y por lo tanto no se especifica.

Entonces, para escribir contenidos de un Java InputStream a una OutputStream, puedes escribir:

input.transferTo(output);

Creo que esto funcionará, pero asegúrate de probarlo..."mejora" menor, pero podría suponer un coste en términos de legibilidad.

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

Usando guayaba ByteStreams.copy():

ByteStreams.copy(inputStream, outputStream);

Función sencilla

Si sólo necesita esto para escribir un InputStream a un File entonces puedes 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 debe usarse cuando tiene varios subprocesos, ya que observado por el Javadoc.

Además, tenga en cuenta que los flujos de entrada y los flujos de salida no envuelven ninguna interrupción de subproceso con IOExceptions...Entonces, deberías considerar incorporar una política de interrupción a tu 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 sería una adición útil si espera utilizar esta API para copiar grandes volúmenes de datos o datos de transmisiones que se atascan durante un tiempo intolerablemente largo.

El JDK usa el mismo código, por lo que parece que no hay una manera "más fácil" sin bibliotecas de terceros torpes (que probablemente no hagan nada diferente de todos modos).Lo siguiente está copiado 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;
    }

Para aquellos que usan marco de primavera hay una útil StreamUtils clase:

StreamUtils.copy(in, out);

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

FileCopyUtils.copy(in, out);

No hay manera de hacer esto mucho más fácil con los métodos JDK, pero como ya señaló Apocalisp, no eres el único con esta idea:Podrías usar IOUtils de Yakarta Commons IO, también tiene muchas otras cosas útiles, que en mi opinión deberían ser parte del JDK...

Usando Java7 y probar con recursos, 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();
    }

Utilice la clase Util de Commons Net:

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

Aquí viene cómo me va con el bucle for más simple.

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

Un fragmento más mínimo en mi humilde opinión (que también tiene un alcance más limitado en 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 for bucle, en lugar de optar por un while con una expresión de asignación y prueba que algunos consideran un estilo "pobre".

Creo que es mejor utilizar un búfer grande, porque la mayoría de los archivos tienen más de 1024 bytes.También es una buena práctica comprobar que el número de bytes leídos sea positivo.

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

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

Otro posible candidato son las utilidades Guava I/O:

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

Pensé en usarlos ya que Guava ya es inmensamente útil en mi proyecto, en lugar de agregar otra biblioteca más para una función.

yo suelo BufferedInputStream y BufferedOutputStream para eliminar la semántica de almacenamiento en búfer del 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;
}

Intentar cactus:

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

Más detalles aquí: http://www.yegor256.com/2017/06/22/entrada-salida-orientada a objetos-en-cactoos.html

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){}
 }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top