为啥要分块写入 Stream?

Posted

技术标签:

【中文标题】为啥要分块写入 Stream?【英文标题】:Why write to Stream in chunks?为什么要分块写入 Stream? 【发布时间】:2012-11-16 10:23:23 【问题描述】:

我想知道为什么这么多示例将字节数组读入流中而不是一次全部...我知道这是一个软问题,但我很感兴趣。

我了解一些关于硬件和填充缓冲区的信息可能非常依赖于大小,并且您不想再次写入缓冲区,直到它被刷新到它需要去的任何地方等等......但是使用 .Net 平台(和其他现代语言)我看到了两者的例子。那么什么时候用哪个,什么时候用,或者说第二个绝对不行?

这是我的意思的东西(代码):

var buffer = new byte[4096];

while (true)

    var read = this.InputStream.Read(buffer, 0, buffer.Length);

    if (read == 0)
        break;

    OutputStream.Write(buffer, 0, read);

而不是:

var buffer = new byte[InputStream.Length];

var read = this.InputStream.Read(buffer, 0, buffer.Length);

OutputStream.Write(buffer, 0, read);

我相信两者都是合法的?那么,为什么要经历所有的 while 循环(无论你决定如何构建它)?

我在这里扮演魔鬼代言人,因为我想尽可能多地学习:)

【问题讨论】:

【参考方案1】:

在第一种情况下,您只需要 4kB 的内存。在第二种情况下,您需要与输入流数据一样多的内存。如果输入流为 4GB,则需要 4GB。

您认为如果文件复制操作需要 4GB 的 RAM 会更好吗?如果您要准备一个 20GB 的磁盘映像怎么办?

管道也有这个东西。您不经常在 Windows 上使用它们,但在其他操作系统上经常会看到类似的情况。第二种情况等待所有数据被读取,然后才将它们写入输出。但是,有时建议尽快写入数据——第一种情况将在读取前 4kB 输入后立即开始写入输出流。考虑为网页提供服务:建议网络服务器尽快发送数据,以便客户端的网络浏览器开始呈现标题和内容的第一部分,而不是等待整个正文。

但是,如果您知道输入流不会大于 4kB,那么这两种情况是等价的。

【讨论】:

一般情况下,您在内存中保存的数量更重要,因此如果您正在填充缓冲区(流)而不移动它,那就不好了。假设我们是否将 OutputStream 排除在等式之外,而只是用 while 循环填充 InputStream?因为我也看到了这个,那会和第二个例子一样糟糕吗? 这完全取决于您的具体情况,取决于您想做什么。有些算法可以对小块进行操作(计算值的总和,找出最大值),有些算法需要所有数据(例如:排序)。在第二种情况下,需要读取所有数据。在第一种情况下——不是真的。 酷......所以它是特定于应用程序的,我认为这是我的主要想法......而不是大多数语言中对象本身的性质。谢谢:)【参考方案2】:

有时,InputStream.Length 对某些源无效,例如来自网络传输,或者缓冲区可能很大,例如从大文件中读取。国际海事组织。

【讨论】:

这是一个非常好的观点......我没有想过这种可能性。但这很有意义,尤其是当您靠近金属并阅读缓冲区接收信息时! +1 ...希望它可以是+2。我想接受这个答案,因为您以一种非常简单的方式提出了我根本没想到的东西。这总是很酷。但为了 SO 社区的利益,最好接受对大多数人有用的答案。【参考方案3】:

它可以保护您免受输入流长达数 GB 的情况。

【讨论】:

保护是什么意思?为什么需要它? 针对例如 OutOfMemoryException 的保护。 对。例如,如果您将文件读取到超出应用程序访问权限的内存中。给定。但这对于任何大量数据都是可能的。因此,模式的块度并不能保护这一点,只是将缓冲区刷新到输出流中并回收。 @tigerswithguitars - “所以模式的厚实并不能保护这一点” - 确实如此。在您的示例中,您只需要一个配置大小 (4096) 的缓冲区,而不是一个大小与输入流一样大的缓冲区。 很酷,我明白了......但你只会拥有你想要的整个文件的一部分,对吗?所以你必须做一些实际的软件工程,并确保这不会让世界崩溃(ed - app die)! :P【参考方案4】:

您不知道Read 可能会返回多少数据。如果您正在读取一个非常大的文件,这可能会造成严重的性能问题。

如果您可以控制输入,并且确定大小合理,那么您当然可以一次读取整个数组。但如果用户可以提供任意输入,则要特别小心。

【讨论】:

以上是关于为啥要分块写入 Stream?的主要内容,如果未能解决你的问题,请参考以下文章

akka-http 发送连续的分块 http 响应(流)

对同一个平面的点云进行分块拟合,拟合出来的法向量为啥方向会不同?

如何在C中逐字节或分块写入()到文件

Java 文件分块及合并

在服务器端处理 plupload 的分块上传

requests分块下载文件