关于java:将InputStream传递给OutputStream的最佳方法

Best way to Pipe InputStream to OutputStream

本问题已经有最佳答案,请猛点这里访问。

我试图找到最好的方法把输入流输送到输出流。我没有选择使用任何其他库,如ApacheIO。这是片段和输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
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文档那样可靠。

It is never correct to use the return value of this method to allocate
a buffer intended to hold all data in this stream.

"字节读取"似乎非常慢。

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


我发现了这一点,最终的阅读可能会引起问题。

建议变更:

1
2
3
4
5
6
7
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更好的固定缓冲区大小。

我不知道…


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

  • 每次都将整个缓冲区写入输出流。对于最后一个块,读取的字节数可能小于1024字节,因此在进行写入时需要考虑到这一点(基本上只写由EDOCX1返回的字节数〔0〕。

  • 在动态缓冲区的情况下,使用available()。这不是一个非常可靠的API调用。在本例中,我不确定在一个循环中它是否可以,但是如果它在一些inputstream实现中被次优化地实现,我不会感到惊讶。

  • 最后一个案例是你向FileInputStream发起的。如果您打算将此作为通用目的,那么就不能使用此方法。


对于寻找一行程序的人,以下是ApacheCommons的一个解决方案:

1
IOUtils.copy(inputStream, outputStream);

此处为文档。有多种不同参数的copy方法。也可以指定缓冲区大小。


java.io包含PipedInputStreamPipedOutputStream

写入输入,它将在输出中以Outputstream的形式可见。事情也可以反过来进行。