Лучший способ передать 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-документами

Никогда не правильно использовать возвращаемое значение этого метода для выделения буфера, предназначенного для хранения всех данных в этом потоке.

«Считывание байта» кажется очень медленным.

Значит, «Фиксированное чтение буфера» - лучший вариант для канала? Есть предположения?


person Sivasubramaniam Arunachalam    schedule 30.07.2012    source источник
comment
Вы имели в виду, что Apache IO лучше?   -  person shareef    schedule 02.04.2015


Ответы (4)


Я бы сказал, что фиксированный размер буфера - лучший / самый простой для понимания. Однако есть несколько проблем.

  • Вы каждый раз записываете весь буфер в выходной поток. Для последнего блока при чтении могло быть прочитано ‹1024 байтов, поэтому вам необходимо принять это во внимание при выполнении записи (в основном записывается только количество байтов, возвращаемых read()

  • В случае динамического буфера вы используете available(). Это не очень надежный вызов API. Я не уверен в этом случае внутри цикла, будет ли это нормально, но я бы не удивился, если бы это было реализовано неоптимально в некоторых реализациях InputStream.

  • Последний случай, который вы переводите на FileInputStream. Если вы хотите, чтобы это было общим назначением, вы не можете использовать этот подход.

person Mike Q    schedule 30.07.2012
comment
Согласен со всеми тремя пунктами. Фиксированный буферный случай необходимо исправить. Динамический буфер MHO просто сломан - я не удивлюсь, если возникнет ситуация, когда available() вернет 0 до того, как будет достигнут конец (т.е. при чтении из сетевого соединения). - person Axel; 30.07.2012
comment
@Mike Q, NIO сейчас для меня не вариант. интерфейс определяет InputStream и OutputStream. - person Sivasubramaniam Arunachalam; 30.07.2012
comment
Обычно я использую фиксированный размер буфера 16 КБ или больше, так как в наши дни у большинства людей есть запасные 16 КБ оперативной памяти :) Кроме того, если возможно, хорошо знать, сколько байтов поступает, чтобы не заканчивать запись слишком рано. - person complistic; 23.01.2013

Я наткнулся на это, и последнее чтение может вызвать проблемы.

ПРЕДЛАГАЕМЫЕ ИЗМЕНЕНИЯ:

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.

ПО МОЕМУ МНЕНИЮ...

person paulsm4    schedule 31.01.2013
comment
Размер буфера должен быть параметром, разные варианты использования требуют разных размеров буфера. - person Aurélien Ooms; 20.08.2014
comment
@ AurélienOoms Нет, они этого не делают. Код в этом ответе будет работать для любого размера буфера больше нуля. - person user207421; 16.02.2017

Java 9

Начиная с Java 9, этот метод можно использовать из InputStream:

public long transferTo(OutputStream out) throws IOException

До Java 9

однострочный из apache commons:

IOUtils.copy(inputStream, outputStream);

Документация здесь. Существует несколько copy методов с разными параметрами. Также возможно указать размер буфера.

person Dariusz    schedule 12.01.2016
comment
Это не отвечает на вопрос. В вопросе говорилось, что в ответе нельзя использовать другие библиотеки. - person Nathan; 03.02.2016
comment
ты прав. он не ответил на вопрос. Но ответ дал мне решение, так как я мог использовать стороннюю организацию. Я бы отредактировал ответ, чтобы признать тот факт, что он не отвечает его потребностям, но это полезный ответ для таких, как я. Так что спасибо. - person Perry Tew; 27.05.2016
comment
Исходная библиотека написана явно с самого начала. Google лучше всего индексирует заголовки, поэтому у этого вопроса довольно много просмотров. Замечание о других библиотеках не так высоко индексируется. - person Dariusz; 28.05.2016
comment
Он может не отвечать на потребности @ OP, но он отвечает моим, и я не могу задать новый вопрос, не так ли? Это было бы просто связано с этим вопросом, а затем мне сказали бы не задавать вопросы, на которые уже был дан ответ. Поскольку поисковые системы присылают нас сюда, есть еще одна причина получить эти дополнительные ответы: они помогают тем, кто не является OP, но имеет почти такую ​​же проблему. - person Haakon Løtveit; 26.08.2019

java.io содержит PipedInputStream и PipedOutputStream

PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream (input);

напишите на вход, и он будет отображаться на выходе как Outputstream. Все может работать и наоборот

person Radu Simionescu    schedule 16.02.2016
comment
OP уже имеет как вход, так и выход - как вы соедините оба конца с этими конвейерными потоками? - person OrangeDog; 01.08.2016
comment
вопрос op на самом деле не о каналах ... он больше о том, как скопировать один файл в другой. Канал больше связан с передачей выходного потока в качестве входного для другого процесса. - person Radu Simionescu; 01.08.2016
comment
писать на ввод - вы имели в виду писать на вывод? - person Llew Vallis; 25.08.2018