将InputStream传递给OutputStream的最佳方法

Siv*_*lam 27 java pipe java-io

我试图找到将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;
}
Run Code Online (Sandbox Code Playgroud)

输出(~4MB输入文件):

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
============================================
Run Code Online (Sandbox Code Playgroud)

'Dynamic Buffer Read'使用available()方法.但根据java docs,它不可靠

使用此方法的返回值来分配用于保存此流中所有数据的缓冲区绝对不正确.

'Byte Read'似乎很慢.

所以'固定缓冲读取'是管道的最佳选择?有什么想法吗?

Mik*_*e Q 12

我想说固定的缓冲区大小是最好/最容易理解的.但是有一些问题.

  • 您每次都将整个缓冲区写入输出流.对于最后的块,读取可能已读取<1024字节,因此在执行写入时需要考虑这一点(基本上只有写入的字节数) read()

  • 在您使用的动态缓冲区中available().这不是一个非常可靠的API调用.在这种情况下,我不确定它是否正常,但如果它在InputStream的某些实现中以次优的方式实现,我不会感到惊讶.

  • 你要投的最后一个案例FileInputStream.如果您打算将此作为一般用途,则不能使用此方法.


pau*_*sm4 12

我遇到了这个,最后的阅读可能会导致问题.

建议更改:

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 ();
Run Code Online (Sandbox Code Playgroud)

我也同意16384可能是比1024更好的固定缓冲区大小.

恕我直言...


Dar*_*usz 10

对于寻找单行的人来说,这是apache commons的解决方案:

IOUtils.copy(inputStream, outputStream);
Run Code Online (Sandbox Code Playgroud)

文档在这里.有多种copy方法具有不同的参数.也可以指定缓冲区大小.

  • 你说得对。他没有回答这个问题。但是,答案确实给了我一个解决方案,因为我可以使用第三方。我会编辑答案以承认它不能满足他的需求这一事实,但对于像我这样的其他人来说这是一个有用的答案。所以我要谢谢你。 (2认同)

Rad*_*scu 7

java.io包含PipedInputStreamPipedOutputStream

PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream (input);
Run Code Online (Sandbox Code Playgroud)

写入输入,它将在输出中显示为Outputstream.事情也可以相反

  • OP 已经有输入和输出 - 如何将两端与这些管道流连接起来? (2认同)