这里的其他答案很棒,但我认为对蒸汽服务的目的进行非常高层次的了解可能会有用。在下面的解释中进行了一些简化,但希望这能传达出这个想法:
什么是流?
流实际上是两个地方之间的数据流,它是管道而不是管道的内容。
一个不好的类比开始
想象一个海水淡化厂(吸收海水、去除盐分并将清洁的饮用水输出到水网):
海水淡化厂无法一次性去除所有海洋中的盐分(我们也不希望它……咸水鱼会住在哪里?),所以我们有:
-
SeaStream 一次将一定量的水吸入植物。
- 那个
SeaStream连接到DesalinationStream去盐分
-
DesalinationStream 的输出连接到DrinkingWaterNetworkStream,将现在的无盐水输出到饮用水源。
好的,那这和电脑有什么关系?
一次移动所有大文件可能会有问题
在计算中,我们经常希望在两个位置之间移动数据,例如从外部硬盘驱动器到数据库中的二进制字段(使用另一个答案中给出的示例)。我们可以通过将文件中的所有数据从位置 A 复制到计算机内存并从那里复制到位置 B 来做到这一点,但是如果文件很大或者源或目标可能不可靠,那么一次移动整个文件可能要么不可行,要么不明智。
例如,假设我们要将 U 盘上的一个大文件移动到数据库中的某个字段。我们可以使用“System.IO.File”对象将整个文件检索到计算机的内存中,然后使用数据库连接将该文件传递到数据库中。
但是,这可能会带来问题,如果文件大于计算机的可用 RAM 怎么办?现在文件可能会被缓存到硬盘上,这很慢,甚至可能会减慢计算机的速度。
同样,如果数据源不可靠,例如使用缓慢且不稳定的 WiFi 连接从网络驱动器复制文件?尝试一次性复制一个大文件可能会令人恼火,因为您获得了一半的文件,然后连接断开,您必须重新开始,否则它可能会再次失败。
最好是拆分文件,一次移动一块
因此,与其一次获取整个文件,不如一次检索一个文件,然后一次将每个文件传递到目标文件。这就是 Stream 所做的,这就是您提到的两种不同类型的流的来源:
- 我们可以使用
FileStream 一次从文件中检索数据
- 和数据库 API 可以提供一个
MemoryStream 端点,我们可以一次写入一个片段。
- 我们将这两个“管道”连接在一起,以将文件片段从文件传输到数据库。
即使文件不是太大而无法保存在 RAM 中,如果没有流,我们仍然会执行一些我们不需要的数字或读/写操作。我们正在执行的阶段是:
- 从磁盘检索数据(慢)
- 写入计算机内存中的 File 对象(快一点)
- 从计算机内存中的 File 对象读取(再次更快)
- 写入数据库(可能很慢,因为管道末端可能有一个旋转的磁盘硬盘驱动器)
Streams 允许我们在概念上取消中间两个阶段,而不是一次将整个文件拖到计算机内存中,我们将操作的输出用于检索数据并将其直接通过管道传递给操作以传递数据到数据库中。
流的其他好处
像这样将数据的检索与数据的写入分开还允许我们在检索数据和传递数据之间执行操作。例如,我们可以添加一个加密阶段,或者我们可以将传入数据写入不止一种类型的输出流(例如,写入 FileStream 和 NetworkStream)。
Streams 还允许我们编写代码,以便在传输中途失败时恢复操作。通过跟踪我们移动的片段数量,如果传输失败(例如,如果网络连接断开),我们可以从我们收到最后一个片段的点重新启动 Stream(这是offset 在BeginRead 方法)。