流是一种针对字节流的操作,它类似于内存与文件之间的一个管道。在对一个文件进行处理时,本质上需要经过借助OS提供的API来进行打开文件,读取文件中的字节流,再关闭文件等操作,其中读取文件的过程就可以看作是字节流的一个过程。.
常见的流类型包括:文件流、终端操作流以及网络Socket等,在.NET中,System.IO.Stream类型被设计为作为所有流类型的虚基类,所有的常见流类型都继承自System.IO.Stream类型,当我们需要自定义一种流类型时,也应该直接或者间接地继承自Stream类型。下图展示了在.NET中常见的流类型以及它们的类型结构:

从上图中可以发现,Stream类型继承自MarshalByRefObject类型,这保证了流类型可以跨越应用程序域进行交互。所有常用的流类型都继承自System.IO.Stream类型,这保证了流类型的同一性,并且屏蔽了底层的一些复杂操作,使用起来非常方便。
下面的代码中展示了如何在.NET中使用FileStream文件流进行简单的文件读写操作:
public class Program
{
private const int bufferlength = 1024;
public static void Main(string[] args)
{
//创建一个文件,并写入内容
string filename = @"C:\TestStream.txt";
string filecontent = GetTestString();
try
{
if (File.Exists(filename))
{
File.Delete(filename);
}
// 创建文件并写入内容
using (FileStream fs = new FileStream(filename, FileMode.Create))
{
Byte[] bytes = Encoding.UTF8.GetBytes(filecontent);
fs.Write(bytes, 0, bytes.Length);
}
// 读取文件并打印出来
using (FileStream fs = new FileStream(filename, FileMode.Open))
{
Byte[] bytes = new Byte[bufferlength];
UTF8Encoding encoding = new UTF8Encoding(true);
while (fs.Read(bytes, 0, bytes.Length) > 0)
{
Console.WriteLine(encoding.GetString(bytes));
}
}
// 循环分批读取打印
//using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
//{
// Byte[] bytes = new Byte[bufferlength];
// int bytesRead;
// do
// {
// bytesRead = fs.Read(bytes, 0, bufferlength);
// Console.WriteLine(Encoding.UTF8.GetString(bytes, 0, bytesRead));
// } while (bytesRead > 0);
//}
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
// 01.取得测试数据
static string GetTestString()
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 10; i++)
{
builder.Append("我是测试数据\r\n");
builder.Append("我是长江" + (i + 1) + "号\r\n");
}
return builder.ToString();
}
}
上述代码的执行结果如下图所示:
在实际开发中,我们经常会遇到需要传递一个比较大的文件,或者事先无法得知文件大小(Length属性抛出异常),因此也就不能创建一个尺寸正好合适的Byte[]数组,此时只能分批读取和写入,每次只读取部分字节,直到文件尾。例如我们需要复制G盘中一个大小为4.4MB的mp3文件到C盘中去,假设我们对大小超过2MB的文件都采用分批读取写入机制,可以通过如下代码实现:
public class Program
{
private const int BufferSize = 10240; // 10 KB
public static void Main(string[] args)
{
string fileName = @"G:\My Musics\BlueMoves.mp3"; // Source 4.4 MB
string copyName = @"C:\BlueMoves-Copy.mp3"; // Destination 4.4 MB
using (Stream source = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
using (Stream target = new FileStream(copyName, FileMode.Create, FileAccess.Write))
{
byte[] buffer = new byte[BufferSize];
int bytesRead;
do
{
// 从源文件中读取指定的10K长度到缓存中
bytesRead = source.Read(buffer, 0, BufferSize);
// 从缓存中写入已读取到的长度到目标文件中
target.Write(buffer, 0, bytesRead);
} while (bytesRead > 0);
}
}
Console.ReadKey();
}
}
上述代码中,设置了缓存buffer大小为10K,即每次只读取10K的内容长度到buffer中,通过循环的多次读写和写入完成整个复制操作。