将 InputStream 传输到 OutputStream 的最佳方法 [重复]

Posted

技术标签:

【中文标题】将 InputStream 传输到 OutputStream 的最佳方法 [重复]【英文标题】:Best way to Pipe InputStream to OutputStream [duplicate] 【发布时间】:2012-07-27 19:05:01 【问题描述】:

我试图找到将 InputStream 传输到 OutputStream 的最佳方法。我没有选择使用任何其他库,如 Apache IO。这是 sn-p 和输出。

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 docs 是不可靠的

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

“字节读取”似乎很慢。

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

【问题讨论】:

你的意思是 apache IO 更好 【参考方案1】:

Java 9

由于 Java 9 可以从InputStream 使用此方法:

public long transferTo(OutputStream out) throws IOException

Java 9 之前

来自apache commons的单线

IOUtils.copy(inputStream, outputStream);

Documentation here。有多个 copy 具有不同参数的方法。也可以指定缓冲区大小。

【讨论】:

这不能回答问题。问题说答案不能使用其他库。 你是对的。他没有回答这个问题。但是,答案确实给了我一个解决方案,因为我可以使用第 3 方。我会编辑答案以承认它不能满足他的需求这一事实,但对于像我这样的其他人来说这是一个有用的答案。非常感谢。 源库是在开始时明确编写的。谷歌索引标题最好,因此这个问题有相当高的观看次数。 “其他图书馆”的评论没有被高度索引。 它可能无法满足@OP 的需求,但它可以满足我的需求,而且我不能很好地提出新问题,可以吗?它只会与这个问题相关联,然后我会被告知不要问已经回答的问题。既然搜索引擎把我们送到这里,就有更多的理由得到这些额外的答案:它们帮助那些不是 OP,但几乎有同样问题的人。【参考方案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 更好的固定缓冲区大小。

恕我直言...

【讨论】:

缓冲区大小应该是一个参数,不同的用例需要不同的缓冲区大小。 @AurélienOoms 不,他们没有。此答案中的代码适用于任何大于零的缓冲区大小。【参考方案3】:

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

您每次都将整个缓冲区写入输出流。对于最后一个块,读取可能已读取 read()返回的字节数

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

您要转换为FileInputStream 的最后一个案例。如果您打算将此作为通用用途,则不能使用此方法。

【讨论】:

三点都同意。固定缓冲箱需要固定。动态缓冲区 MHO 刚刚损坏 - 如果出现 available() 在到达终点之前返回 0(即从网络连接读取时)的情况,我不会感到惊讶。 @Mike Q,蔚来现在不是我的选择。该接口定义了 InputStream 和 OutputStream。 我通常使用 16k 或更大的固定缓冲区大小,因为现在大多数人都有 16k 的备用内存 :) 另外,如果可能的话,最好知道有多少字节进入,所以你不要'不要过早结束写作。【参考方案4】:

java.io 包含 PipedInputStreamPipedOutputStream

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

写入输入,它将在输出中显示为Outputstream。反之亦然

【讨论】:

OP 已经有输入和输出 - 你如何用这些管道流连接两端? 操作问题实际上与管道无关......更多的是关于如何将一个文件复制到另一个文件。管道更多的是关于将输出流作为另一个进程的输入流传递。 “写入输入” - 您的意思是写入输出吗?

以上是关于将 InputStream 传输到 OutputStream 的最佳方法 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

JavaEE基础(二十)/IO流

将文件和数据转换为 outputStream 并将 inputStream 转换为文件和数据

将文件从容器发送到主机

如何从 Java 8 Streams 中获取 Inputstream?

在Android上的蓝牙中从InputStream读取数据时出错

如何将多个不同的InputStream链接到一个InputStream中