Лучший способ подключения InputStream к OutputStream
Мне нужно было найти лучший способ подключить InputStream к OutputStream. У меня нет возможности использовать другие библиотеки, такие как Apache IO. Вот фрагмент и вывод.
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;
}
Выход (входной файл ~ 4 МБ):
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
============================================
"Чтение динамического буфера" использует метод available()
. Но он не является надежным в соответствии с java docs
Неправильно использовать возвращаемое значение этого метода для выделения буфер, предназначенный для хранения всех данных в этом потоке.
'Byte Read' кажется очень медленным.
Итак, "Fixed Buffer Read" - лучший вариант для трубы? Любые мысли?
Ответы
Ответ 1
Я бы сказал, что фиксированный размер буфера лучше/проще всего понять. Однако есть несколько проблем.
-
Вы каждый раз записываете весь буфер в выходной поток. Для конечного блока считывание может быть прочитано < 1024 байта, поэтому вам нужно учитывать это при выполнении записи (в основном записывать только количество байтов, возвращаемых read()
-
В случае с динамическим буфером вы используете available()
. Это не очень надежный вызов API. Я не уверен в этом случае внутри цикла, будет ли это нормально, но я не был бы удивлен, если бы он был реализован под оптимальным образом в некоторых реализациях InputStream.
-
В последнем случае вы выполняете FileInputStream
. Если вы намереваетесь, чтобы это было общей целью, вы не можете использовать этот подход.
Ответ 2
Я наткнулся на это, и окончательное чтение может вызвать проблемы.
ПРЕДЛАГАЕМЫЕ ИЗМЕНЕНИЯ:
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 ();
Я также согласен с тем, что 16384, вероятно, лучше фиксированный размер буфера, чем 1024.
ИМХО...
Ответ 3
Для людей, ищущих однострочный, вот решение от apache commons:
IOUtils.copy(inputStream, outputStream);
Документация здесь. Существует несколько методов copy
с разными параметрами. Также можно указать размер буфера.
Ответ 4
java.io
содержит PipedInputStream
и PipedOutputStream
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream (input);
записать на вход, и он будет отображаться на выходе как Outputstream
. Вещи могут работать и наоборот.