java - La mejor manera de canalizar InputStream a OutputStream
pipe java-io (4)
Esta pregunta ya tiene una respuesta aquí:
Estaba tratando de encontrar la mejor manera de canalizar InputStream a OutputStream. No tengo la opción de usar ninguna otra biblioteca como Apache IO. Aquí está el fragmento y la salida.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
public class Pipe {
public static void main(String[] args) throws Exception {
for(PipeTestCase testCase : testCases) {
System.out.println(testCase.getApproach());
InputStream is = new FileInputStream("D://in//lft_.txt");
OutputStream os = new FileOutputStream("D://in//out.txt");
long start = System.currentTimeMillis();
testCase.pipe(is, os);
long end = System.currentTimeMillis();
System.out.println("Execution Time = " + (end - start) + " millis");
System.out.println("============================================");
is.close();
os.close();
}
}
private static PipeTestCase[] testCases = {
new PipeTestCase("Fixed Buffer Read") {
@Override
public void pipe(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[1024];
while(is.read(buffer) > -1) {
os.write(buffer);
}
}
},
new PipeTestCase("dynamic Buffer Read") {
@Override
public void pipe(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[is.available()];
while(is.read(buffer) > -1) {
os.write(buffer);
buffer = new byte[is.available() + 1];
}
}
},
new PipeTestCase("Byte Read") {
@Override
public void pipe(InputStream is, OutputStream os) throws IOException {
int c;
while((c = is.read()) > -1) {
os.write(c);
}
}
},
new PipeTestCase("NIO Read") {
@Override
public void pipe(InputStream is, OutputStream os) throws IOException {
FileChannel source = ((FileInputStream) is).getChannel();
FileChannel destnation = ((FileOutputStream) os).getChannel();
destnation.transferFrom(source, 0, source.size());
}
},
};
}
abstract class PipeTestCase {
private String approach;
public PipeTestCase( final String approach) {
this.approach = approach;
}
public String getApproach() {
return approach;
}
public abstract void pipe(InputStream is, OutputStream os) throws IOException;
}
Salida (~ 4MB de archivo de entrada):
Fixed Buffer Read
Execution Time = 71 millis
============================================
dynamic Buffer Read
Execution Time = 167 millis
============================================
Byte Read
Execution Time = 29124 millis
============================================
NIO Read
Execution Time = 125 millis
============================================
''Dynamic Buffer Read'' utiliza el método available()
. Pero no es fiable según los documentos de Java
Nunca es correcto usar el valor de retorno de este método para asignar un búfer destinado a contener todos los datos en esta secuencia.
''Byte Read'' parece ser muy lento.
Entonces, ¿''Lectura de búfer fijo'' es la mejor opción para tuberías? ¿Alguna idea?
Me encontré con esto, y la lectura final puede causar problemas.
CAMBIO SUGERIDO:
public void pipe(InputStream is, OutputStream os) throws IOException {
int n;
byte[] buffer = new byte[1024];
while((n = is.read(buffer)) > -1) {
os.write(buffer, 0, n); // Don''t allow any extra bytes to creep in, final write
}
os.close ();
También estoy de acuerdo en que 16384 es probablemente un tamaño de búfer fijo mejor que 1024.
EN MI HUMILDE OPINIÓN...
Para las personas que buscan una sola línea , aquí hay una solución de apache commons :
IOUtils.copy(inputStream, outputStream);
Documentación aquí . Existen múltiples métodos de copy
con diferentes parámetros. También es posible especificar el tamaño del búfer.
Yo diría que un tamaño de búfer fijo es el mejor / más fácil de entender. Sin embargo hay algunos problemas.
Estás escribiendo todo el búfer en el flujo de salida cada vez. Para el bloque final, la lectura puede haber leído <1024 bytes, por lo que debe tener esto en cuenta al realizar la escritura (básicamente, solo escriba el número de bytes devueltos por
read()
En el caso de búfer dinámico utiliza
available()
. Esta no es una llamada API terriblemente confiable. No estoy seguro en este caso dentro de un bucle si estará bien, pero no me sorprendería si se implementó de forma sub-óptima en algunas implementaciones de InputStream.El último caso que estás
FileInputStream
aFileInputStream
. Si pretende que esto sea un propósito general, no puede utilizar este enfoque.
PipedInputStream
contiene PipedInputStream
y PipedOutputStream
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream (input);
escriba en la entrada y será visible en la salida como un Outputstream
. Las cosas pueden funcionar al revés también