将 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;
}

输出(~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
============================================

“动态缓冲区读取”使用 available() 方法。但根据 java 文档,它不可靠

使用此方法的返回值来分配旨在保存此流中所有数据的缓冲区是永远不正确的。

“字节读取”似乎很慢。

那么“固定缓冲区读取”是管道的最佳选择吗?有什么想法吗?

原文由 Siva Arunachalam 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.1k
2 个回答

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

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

  • 在动态缓冲区情况下,您使用 available() 。这不是一个非常可靠的 API 调用。在这种情况下,我不确定在循环内是否可以,但如果它在 InputStream 的某些实现中未达到最佳实现,我不会感到惊讶。

  • 您投射到的最后一个案例 FileInputStream 。如果您打算将此作为通用目的,那么您不能使用这种方法。

原文由 Mike Q 发布,翻译遵循 CC BY-SA 3.0 许可协议

Java 9

由于 Java 9 可以使用来自 InputStream这种方法

 public long transferTo(OutputStream out) throws IOException

Java 9 之前

来自 apache commons 的单行 代码:

 IOUtils.copy(inputStream, outputStream);

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

原文由 Dariusz 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题