Stream实战

1. Stream概述

在夜晚,仰望星空的时候,你只能看到星星和月亮。在Stream的世界里,你只能看到无数的0和1组成的二进制数据(byte)。我们在网络中传输的数据,其实都是以一组有序的byte的形式传输的。

.net framework中定义了抽象类Stream,它封装了一组有序的字节(byte数组),并提供了一些属性和一些方法,用于处理里面的有序字节组。由于Stream是抽象类,它不可以被直接初始化,但是可以从它的子类来初始化一个Stream对象。.net framework中已经默认实现了很多种不同类型的XXXStream都扩展自Stream。

Stream类提供了基本的Read,Write方法,用来处理流。Read方法可以从流中读取数据,Write方法可以把数据写入流中:

在Read和Write方法中,在流的什么位置读取和写入,都是看Position的值,它表明了流中当前的位置。

Seek方法可以重新设置Position的值。

Stream中的属性:

CanRead: 表明当前stream是否可以Read操作。

CanSeek: 表明当前stream是否可以Seek操作。

CanWrite: 表明当前stream是否可以Write操作。

Length: 返回封装在里面的byte[]的长度。

Position: 表明当前stream中Position指针位置。

Stream中的方法:

//把保存在缓冲区里面的数据写入硬盘上,并清空缓冲区的数据。
void Flush();
//buffer: 读取Stream的数据,保存到buffer中。
//offset: buffer数组的起始位置,取值范围是: 0 到 buffer.Length-1。
//count: buffer数组从offset位置开始保存流的count个字节数据,,最大值不超过buffer.Length - offset。
int Read(byte[] buffer, int offset, int count); 

//设置Position指针位置。SeekOrigin枚举值:Begin,Current,End。
long Seek(long offset, SeekOrigin origin);

//buffer: 即将写入Stream的二进制数据。
//offset: buffer数组的起始位置,取值范围是: 0 到 buffer.Length-1
//count: buffer数组从offset位置开始之后的count个字节数据写入流,最大值不超过buffer.Length - offset
void Write(byte[] buffer, int offset, int count);

//使用完成之后,需要关闭Stream。
Dispose();
Close();

在C#中已经默认实现了很多Stream,各有各的用途。例如:

BufferedStream: A utility stream, it wrapped another stream, which helps improve performance.

FileStream: Stream used to read and write data to the file.

MemoryStream: Creates a stream whose backing store is memory.

CryptoStream: Defines a stream that links data streams to cryptographic transformations.

UnmanagedMemoryStream,IsolatedStorageFileStream,PipeStream,NetworkStream,DeflateStream,GZipStream等等。

2. Stream的基本示例

2.1- Write stream example

流的写入练习,把byte或byte数组写入文件:

// Create directory.
string dir = @"C:\temp";
Directory.CreateDirectory(dir);

string filePath = dir + @"\MyTest.txt";

// Create Stream object via Constructor of FileStream.
// FileMode.Create: Create file, if it already exists, it will be overwritten.
using (Stream writingStream = new FileStream(filePath, FileMode.Create))
{
    if (writingStream.CanWrite)
    {
        // A byte array. (1byte < 2^8)
        // This array corresponds to: {'H','e','l','l','o',' ','W','o','r','l','d'}.
        // buffer: 即将写入Stream的二进制数据。
        byte[] buffer = new byte[] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100 };
        // offset: 设置buffer数组的起始位置,取值范围是: 0 到 buffer.Length-1
        int offset = 0;
        // count: 从buffer数组offset位置起,一共需要写入的数量,最大值不超过buffer.Length - offset
        int count = buffer.Length - offset;
        writingStream.Write(buffer, offset, count);

        // Write a byte (33 = '!')
        writingStream.WriteByte(33);
        // Write a byte (33 = ' ')
        writingStream.WriteByte(32);
    }
}

2.2- Read stream example

流的读取练习,从文件中读取字节信息,并转换成字符串:

string path = @"C:\temp\MyTest.txt";

if (!File.Exists(path))
{
    Console.WriteLine("File " + path + " does not exists!");
    return;
}

// Create Stream object via constructor of FileStream
// FileMode.Open: Open file to read.
using (Stream readingStream = new FileStream(path, FileMode.Open))
{
    UTF8Encoding encoding = new UTF8Encoding(true);
    StringBuilder sb = new StringBuilder();

    // 从Stream中读取的数据,会保存在buffer中
    byte[] buffer = new byte[10];
    // offset: 设置buffer数组的起始位置,取值范围是: 0 到 buffer.Length-1
    int offset = 2;
    // count: 从buffer数组offset位置起,一共需要读取的数量,最大值不超过buffer.Length - offset
    int count = buffer.Length - offset;
    // Read on stream and asign to temporary array.
    // Returns the number of bytes read.
    int countOfReaded = 0;
    while ((countOfReaded = readingStream.Read(buffer, offset, count)) > 0)
    {
        string s = encoding.GetString(buffer, offset, countOfReaded);
        sb.Append(s);
    }
    Console.WriteLine(sb.ToString());
}

3- FileStream

Filestream is a class which extends from the Stream class, FileStream is used to read and write data to file, it inherits the properties, methods of Stream, and has additional functions for reading and recording data on the file.

There are some read-write mode:

FileMode

Append:Opens the file if it exists and seeks to the end of the file, or creates a new file.

Create:Specifies that the operating system should create a new file. If the file already exists, it will be overwritten.

CreateNew:Specifies that the operating system should create a new file.If the file already exists, an IOException exception is thrown. This mode requires FileIOPermissionAccess.Write permission.

Open:Specifies that the operating system should open an existing file. A System.IO.FileNotFoundException exception is thrown if the file does not exist.

OpenOrCreate:Specifies that the operating system should open a file if it exists; otherwise, a new file should be created

Truncate: Specifies that the operating system should open an existing file. When the file is opened, it should be truncated so that its size is zero bytes.

FileMode example:

string path = @"C:\temp\MyTest.txt";

if (!File.Exists(path))
{
    Console.WriteLine("File " + path + " does not exists!");
    // Ensure that the parent directory exists.
    Directory.CreateDirectory(@"C:\temp");
}

// Create a FileStream to write file.
// (FileMode.Append: Open file to write to end of file,
// if file does not exit, create it and write).
using (FileStream writeFileStream = new FileStream(path, FileMode.Append))
{
    string s = "\nHello every body!";
    // Convert string to bytes with UTF8 encoding.
    byte[] bytes = Encoding.UTF8.GetBytes(s);
    // Write bytes to file.
    writeFileStream.Write(bytes, 0, bytes.Length);
}
Console.WriteLine("Finish!");

Constructor:

There are 11 constructors on FileStream class (Excluding the constructor obsoleted) used to initialize a FileStream object:

FileStream Constructors:

FileStream(SafeFileHandle, FileAccess)     
FileStream(SafeFileHandle, FileAccess, Int32)
FileStream(SafeFileHandle, FileAccess, Int32, Boolean)     
FileStream(String, FileMode)
FileStream(String, FileMode, FileAccess)
FileStream(String, FileMode, FileAccess, FileShare)
FileStream(String, FileMode, FileAccess, FileShare, Int32)
FileStream(String, FileMode, FileAccess, FileShare, Int32, Boolean)
FileStream(String, FileMode, FileAccess, FileShare, Int32, FileOptions)     
FileStream(String, FileMode, FileSystemRights, FileShare, Int32, FileOptions)
FileStream(String, FileMode, FileSystemRights, FileShare, Int32, FileOptions, FileSecurity)

However, you also have other ways to create a FileStream object, such as through the FileInfo, this is the class that represents a file in the system. Methods of FileInfo, returns FileStream:

// By default, full read/write access to new files is granted to all users.
Create(); 
// Opens a file in the specified mode.
Open(FileMode); 
// Opens a file in the specified mode with read, write, or read/write access.
Open(FileMode, FileAccess);
// Opens a file in the specified mode with read, write, or read/write access and the specified sharing option.
Open(FileMode, FileAccess, FileShare);
// Creates a write-only FileStream.
OpenWrite();
// Creates a read-only FileStream.
OpenRead();

Create FileStream via FileInfo:

FileInfo afile = new FileInfo(@"C:\temp\MyTest.txt");
if (!afile.Exists)
{
    Console.WriteLine("File does not exist!");
    Console.Read();
    return;
}

using (FileStream stream = afile.Open(FileMode.OpenOrCreate))
{
    stream.Seek(0, SeekOrigin.End);

    string s = "\nNew line text";
    byte[] bytes = Encoding.UTF8.GetBytes(s);
    stream.Write(bytes, 0, bytes.Length);
}

Console.WriteLine("Finished!");
Console.Read();

4- BufferedStream

BufferedStream is a class which extends from the Stream class, it's a buffer stream which wrap a different stream and help to improve the efficiency of reading and writting data.

BufferedStream only two constructors, it wrap a different Stream.

Constructor:

//Initializes a new instance of the BufferedStream class with a default buffer size of 4096 bytes.
BufferedStream(Stream); 
//Initializes a new instance of the BufferedStream class with the specified buffer size.
BufferedStream(Stream, Int32);

I put out a situation, you create a BufferedStream wrap FileStream, for the purpose of writing data to the file. The data written to the stream buffer will be located temporarily in memory, and when the buffer is full, the data is automatically flushed to file, you can proactively flush data to the file by using the Flush() method. Using BufferedStream in this case reduces the number of times to write down the drive, and so it increases the efficiency of the program.

For example, one BufferedStream wrap a stream to write data to file:

string fileName = @"C:\temp\MyFile.txt";
FileInfo file = new FileInfo(fileName);

// Make sure the directory exists.
file.Directory.Create();

using (FileStream fileStream = file.Open(FileMode.OpenOrCreate))
{
    // Create BufferedStream wrap the FileStream.
    // (Specify the buffer is 10000 bytes).
    using (BufferedStream bs = new BufferedStream(fileStream, 10000))
    {
        bs.Seek(0, SeekOrigin.End);

        for (int index = 1; index <= 2000; index++)
        {
            String s = "This is line " + index + "\n";

            byte[] bytes = Encoding.UTF8.GetBytes(s);

            // Write to buffer, when the buffer is full it will 
            // automatically push down the file.
            bs.Write(bytes, 0, bytes.Length);
        }

        // Flushing the remaining data in the buffer to the file.
        bs.Flush();
    }
}

Console.WriteLine("Finished!");
Console.Read();

5- MemoryStream

MemoryStream class is directly extended from the Stream class, it is the stream which data is stored on the memory.

Essentially, MemoryStream is an object that manages a buffer is an array of bytes, while the bytes are written to this stream will automatically be assigned to the next position from the current position of the cursor on the array. When the buffer is full a new array with a larger size to be created, and copy the data from the old array.

Constructor:

MemoryStream()   
MemoryStream(Byte[] buffer)
MemoryStream(Byte[] buffer, Boolean writable)
MemoryStream(Byte[] buffer, Int32 index, Int32 count, Boolean writable) 
MemoryStream(Byte[] buffer, Int32 index, Int32 count, Boolean, Boolean publiclyVisible)  
MemoryStream(Byte[], Int32, Int32, Boolean, Boolean)  
MemoryStream(Int32 capacity)

Example:

public static void DoDemo()
{
    Demo1();
    Demo2();
    Console.WriteLine("Finish!");
    Console.Read();
}

private static void Demo1()
{
    // Create MemoryStream object with capacity of 100 bytes.
    MemoryStream memoryStream = new MemoryStream(100);

    byte[] javaBytes = Encoding.UTF8.GetBytes("Java");
    byte[] csharpBytes = Encoding.UTF8.GetBytes("CSharp");

    // Write bytes to memory stream.
    memoryStream.Write(javaBytes, 0, javaBytes.Length);
    memoryStream.Write(csharpBytes, 0, csharpBytes.Length);

    // Write out capacity and length.
    // ==> Capacity: 100, Length: 10.
    Console.WriteLine("Capacity: {0} , Length: {1}",
                          memoryStream.Capacity.ToString(),
                          memoryStream.Length.ToString());

    // At this time the cursor position is standing after the character 'p'.
    // ==> 10.
    Console.WriteLine("Position: " + memoryStream.Position);

    // Move the cursor backward 6 bytes, compared to the current position.
    memoryStream.Seek(-6, SeekOrigin.Current);

    // Now cursor position after the character 'a' and before 'C'.
    // ==> 4
    Console.WriteLine("Position: " + memoryStream.Position);

    byte[] vsBytes = Encoding.UTF8.GetBytes(" vs ");

    // Write to memory stream.
    memoryStream.Write(vsBytes, 0, vsBytes.Length);

    byte[] allBytes = memoryStream.GetBuffer();

    string data = Encoding.UTF8.GetString(allBytes);

    // ==> Java vs rp
    Console.WriteLine(data);
}

private static void Demo2()
{
    byte[] buffer = Encoding.UTF8.GetBytes("Java1234");
    int index = 3;
    int count = buffer.Length - index;
    // buffer会被保存在buffer字段里面,publiclyVisible设置true,通过GetBuffer()就可以访问
    // index可以指定buffer数组的起始位置,取值范围是: 0 到 buffer.Length-1
    // count: 从buffer数组起始位置起,一共需要读取的数量,最大值不超过buffer.Length - index
    MemoryStream memoryStream = new MemoryStream(buffer, index, count, true, true);

    byte[] readBytes = new byte[8];
    memoryStream.Read(readBytes, 0, readBytes.Length);
    string readData = Encoding.UTF8.GetString(readBytes);
    Console.WriteLine(readData);

    memoryStream.Seek(0, SeekOrigin.Begin);
    byte[] readBytes2 = new byte[8];
    memoryStream.Read(readBytes2, 0, readBytes2.Length);
    string readData2 = Encoding.UTF8.GetString(readBytes2);
    Console.WriteLine(readData2);

    byte[] allBytes = memoryStream.GetBuffer();
    string data = Encoding.UTF8.GetString(allBytes);
    Console.WriteLine(data);
}

6- UnmanagedMemoryStream

UnmanagedMemoryStream lets you read the data stream that is not managed without copying all of them to manage the Heap memory before using. It helps you save memory if you are having to deal with lots of data.

Note that there is a limit of 2GB for MemoryStream so you have to use the UnmanagedMemoryStream if you exceed this limit.

I put out a situation: There is discrete data available on the memory. And you can gather to manage by UnmanagedMemoryStream by managing the pointers of the aforementioned discrete data, instead of copying them to the stream to manage.

Constructor:

UnmanagedMemoryStream()
UnmanagedMemoryStream(Byte* pointer, Int64 length)
UnmanagedMemoryStream(Byte* pointer, Int64 length, Int64 capacity, FileAccess access)
UnmanagedMemoryStream(SafeBuffer buffer, Int64 offset, Int64 length)  
UnmanagedMemoryStream(SafeBuffer buffer, Int64 offset, Int64 length, FileAccess access)

7- CryptoStream

CryptoStream is a class used for data stream encryption.

The image below illustrates CryptoStream wrap another stream (such as writting stream file), when you write byte data to CryptoStream, bytes will be encrypted into another bytes before flushing to stream that is written to the file . Now the content of the file has been encrypted.

Note that you can choose an encryption algorithm when creating CryptoStream object.

In a reverse situation, CryptoStream wrap a reading stream file (file whose contents were encrypted above), the byte in FileStream has been encrypted, it will be decrypted by CryptoStream.

Another important thing you need to remember is that, not all of encryption algorithm have 2-way encryption and decryption solutions.

Let's look at an example:

Here I use the DES algorithm to encrypt and decrypt, you need to provide 128-bit array which is the key of your security.

// Provides DES encryption algorithm.
DESCryptoServiceProvider provider = new DESCryptoServiceProvider();

// Your secret key (Must by 128bits = 8bytes).
// (Equivalent to 8 ASCII characters).
provider.Key = Encoding.UTF8.GetBytes("1234abcd");
provider.IV = Encoding.UTF8.GetBytes("12345678");

string encryedFile = @"C:\temp\EncryptedFile.txt";

// A stream to write file.
using (FileStream stream = new FileStream(encryedFile, FileMode.OpenOrCreate, FileAccess.Write))
{
    // Encryptor.
    ICryptoTransform encryptor = provider.CreateEncryptor();

    // Create CryptoStream wrap the FileStream.
    // (FileStream in this case used to write the file).
    using (CryptoStream cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
    {
        // A byte array is not encrypted.
        byte[] data = Encoding.UTF8.GetBytes("Bear, I love you. OK?");

        // Write to cryptoStream.
        cryptoStream.Write(data, 0, data.Length);
    }
}
Console.WriteLine("Write to file: " + encryedFile);

// Next read the encrypted file has been created above.
using (FileStream stream = new FileStream(encryedFile, FileMode.Open, FileAccess.Read))
{
    // Decryptor.
    ICryptoTransform decryptor = provider.CreateDecryptor();

    // A CryptoStream wrap to FileStream.
    // (FileStream in this case used to read the file).
    using (CryptoStream cryptoStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Read))
    {
        byte[] temp = new byte[1024];
        int read = 0;
        while ((read = cryptoStream.Read(temp, 0, temp.Length)) > 0)
        {
            String s = Encoding.UTF8.GetString(temp, 0, read);

            Console.Write(s);
        }
    }
}

// Finished
Console.Read();

Running the example:

View the contents of the newly created file.

本文分享自微信公众号 - 明丰随笔(liumingfengwx2),作者:刘明丰

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 浅谈.Net Framework中压缩和解压

    ZipFile类是一个工具类,它有许多静态方法,可以帮助打开zip文件、提取数据、将目录压缩成zip文件、将zip文件提取到文件夹等等。

    小蜜蜂
  • 使用MiniProfiler小结

    可以对一个页面本身,及该页面通过直接引用、Ajax、Iframe形式访问的其它页面进行监控。

    小蜜蜂
  • 用.Net Framework标准使用事件

    我们遵循.NET Framework标准事件模式的添加到类和结构中,.NET Framework 类库中的所有事件均基于 EventHandler 委托,定义如...

    小蜜蜂
  • 美国:关于人工智能合规的舆论(Computer and Society)

    人工智能(AI)具有广泛的社会影响,但社会科学家只是刚刚开始研究公众对这项技术的态度。现有研究发现,公众对机构的信任可以在新兴技术的监管中发挥重要作用。通过一项...

    李欣颖6837176
  • 【翻译】在Visual Studio中使用Asp.Net Core MVC创建第一个Web Api应用(二)

    运行应用 In Visual Studio, press CTRL+F5 to launch the app. Visual Studio launches a...

    脑洞的蜂蜜
  • NGINX Server Push服务器推送

    Support for HTTP/2 server push is also included in NGINX Plus R15.

    javascript.shop
  • GDB调试Segmentation Fault

    http://www.unknownroad.com/rtfm/gdbtut/gdbsegfault.html

    bear_fish
  • 共享单车困兽之斗:大数据或成破解之法

    孟永辉
  • Gym 100952I&&2015 HIAST Collegiate Programming Contest I. Mancala【模拟】

    I. Mancala time limit per test:3 seconds memory limit per test:256 megabytes inp...

    Angel_Kitty
  • alert日志中出现ash size的警告

    今天查看数据库的alert日志总出现了如下的警告。 Archived Log entry 202 added for thread 1 sequence 2...

    jeanron100

扫码关注云+社区

领取腾讯云代金券