Подтвердить что ты не робот

Лучший способ подключения 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" - лучший вариант для трубы? Любые мысли?

4b9b3361

Ответ 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. Вещи могут работать и наоборот.