FileStream相关知识分享

1、如何理解FIleStream数组

经过前3章的学些,相信你们对于Stream已经有必定的了解,可是又如何去理解FileStream呢?请看下图:安全

 

 咱们磁盘中的任何文件都是经过二进制数组组成,最为直观的就是记事本了,当咱们新建一个记事本时,它的大小时0KB,咱们每次输入一个数字或字母时,文件便会自动增大到4KB,可见,随着咱们输入的内容愈来愈多,文件也会愈来愈大,同理,当咱们删除文件内容时,文件也会相应的减少,对了,聪明的你确定会问:谁将内容以怎样的形式放到文件中去了?服务器

好问题,还记得第一章流的概念嘛?对了,真实世界的一群鱼能够经过河流往前往各个地方,FileStream也同样,byte能够经过FileStream进行传输,这样咱们便能在计算机上对任何文件进行一系列操做了。网络

2、FileStream的重要性异步

FileStream顾名思义文件流,咱们电脑上的文件均可以经过文件流进行操做,例如文件的复制、简介、粘贴、删除、本地文件上传、下载、等许多重要的功能都离不开文件流。因此文件流不只在本机上很是重要,在现在的网络世界上也万万不能缺乏的,想象一下咱们开启虚拟机后,直接从本地复制一个文件到虚拟机上,时多么的方便,若是没有文件流,这个将不可思议。(你们别误解,文件流经过网络流将客户都安上传的文件传到服务器端接收,而后经过文件流进行处理,下载正好相反)async

3、FileStream经常使用构造函数介绍函数

一、FileStream(SafeFileHandle safeFileHandle,FileAccess fileAccess)测试

非托管参数SafeFileHandle简单介绍google

SafeFileHandle:是一个文件安全句柄,这样的解释可能你们一头雾水,别急,你们先不要去理睬这深邃的含义,只要知道这个类型是C#非托管资源,也就是说它可以调用非托管资源的方法,并且不属于C#回收机制,因此咱们必须使用GC手动或其余方式(Finalize或Dispose方法)进行非托管资源的回收,因此SafeFileHandle是一个默默无闻的保镖,一直暗中保护FileStream和文件的安全,为了让你们更好的理解这个保镖,请看第一段代码:spa

 

1         static void Main(string[] args)
2         {
3             var rootPath = Environment.CurrentDirectory;
4             var fileName = Path.Combine(rootPath, "TextFile1.txt");//@"TextFile1.txt";
5             FileStream fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
6             Console.ReadLine();
7             File.Delete(fileName);
8             Console.ReadKey();
9         }

咱们运行一下,结果报错了,我看看一下错误:

 

 为何会报错呢?其实程序被卡在了Console.ReadLine()这里,FileStream并无被释放,系统不知道这个文件是否还有用,因此帮咱们保护这个文件(那个非托管资源SafeFileHandle所使用的内存还被占用着)因此SafeFileHandle在内部保护了这个文件从而报出了这个异常,若是咱们将流关闭后,这个问题就不存在了。

因此,咱们又回到了一个老问题上面,咱们每次使用完FileStream后都必须将他关闭并释放资源。

二、FileStream(string str,FileModel model)

string 参数表示文件所在的地址,FileMode是个枚举,表示肯定如何打开或建立文件 。

FileModel枚举参数包含如下内容:

 

 三、FileStream(IntPtr intPtr,FIleAccess fileAccess,Boolean ownsHandle)

FileAccess参数也是一个枚举,表示对该文件的操做权限:

 

 参数ownsHandle:也就是相似于前面和你们介绍的SafeFileHandler,有2点必须注意:(1)对于指定的文件句柄,操做系统不容许所请求的access,例如:当access为Write或ReadWrite而文件句柄设置为只读访问的时候,会出现异常。因此ownsHandle才是老大,FileAccess的权限应该在ownsHandle的范围内。(2)FileStream假定它的句柄有独占控制权,当FileStream也持有句柄时,读取、写入或查找可能会致使数据破坏,为了数据安全,请使用句柄前调用Flush,并避免在使用完句柄后调用Close之外的任何方法。

四、FileStream(string str,FileModel model,FileAccess,fileAccess,FileShare fileShare)

FileShare:一样时一个枚举类型,肯定文件如何由进程共享。

 

五、FileStream(string str,FileMode mode,FileAccess fileAccess,FileShare fileShare,Int32 i,Boolean async)

Int32:这是一个缓冲区的大小,你们能够按照本身的须要定制;

Boolean async:是否异步读写,告诉FileStream示例,是否采用异步读写

六、FileStream(string str,FileMode mode,FileShare fileShare,Int32 i,FileOption fileOption)

FileOption:这是相似于FileStream对于我呢见操做的高级选项

4、FileStream经常使用属性介绍

一、CanRead:指示FileStream是否能够读操做

二、CanSeek:指示FileStream是否能够跟踪查找流操做

三、IsAsync:FileStream是否同步工做仍是异步工做

四、Name:FileStream的名字,只读属性

五、ReadTimeout:设置读取超时时间

六、SafeFileHandle:文件安全句柄,只读属性

七、Position:当前FileStream所在的流的位置

5、FileStream经常使用方法介绍

如下方法重写了Stream的一些虚方法

一、IAsyncResult BeginRead 异步读取

二、IAsyncResult BeginWrite 异步写

三、void Close 关闭当前FileStream

四、void EndRead 异步读取结束

五、void EndWrite 异步写结束

六、void Flush 马上释放缓冲区,将数据所有导出到基础流(文件)中

七、int Read 通常读取

八、int ReadByte 读取单个字节

九、long Seek 跟踪查找流所在的位置

十、void SetLength 设置FileStream的长度

十一、void Write 通常写

十二、void WriteByte 写入单个字节

6、属于FileStream独有的方法

一、FileSecurity GetAccessControl()

这个不是很经常使用,FileSecurity时文件安全类,直接表达当前文件的访问控制列表(ACL)的复合当前文件权限的项目,ACL你们有个了解就行,之后会单独和你们讨论下ACL方面的知识

二、void Lock(long position,long length)

这个Lock方法和线程中的Lock关键字很不同,它可以锁住文件中的某一部分,很是的强悍!用了这个方法咱们可以精确锁定住咱们要锁住的文件的部份内容

三、void SetAccessControl(FileSecurity fileSecurity)

和GetAccessControl很类似,ACL技术会再之后单独介绍

四、void Unlock(long position,long length)

正好和lock方法相反,对于文件部分的解锁

7、文件的新建和拷贝(主要演示文件同步和异步操做)

首先咱们尝试DIY一个IFileCOnfig

 

1     public interface IFileConfig
2     {
3         string FileName { get; set; }
4         bool IsAsync { get; set; }
5     }

 建立文件配置类CreateFileConfig,用于添加文件一些配置设置,实现添加文件的操做

 1     public class CreateFileConfig : IFileConfig
 2     {
 3         /// <summary>
 4         /// 文件名称
 5         /// </summary>
 6         public string FileName { get; set; }
 7         /// <summary>
 8         /// 是否异步
 9         /// </summary>
10         public bool IsAsync { get; set; }
11         /// <summary>
12         /// 建立文件所在Url
13         /// </summary>
14         public string CreateUrl { get; set; }
15     }

让咱们定义一个文件流测试类:FileStreamTest来实现文件的操做。

1     /// <summary>
2     /// 文件测试类
3     /// </summary>
4     public class FileStreamTest

在该类中实现一个简单的Create方法,用来同步或异步的实现添加文件,FileStream会根据配置类去选择相应的构造函数,实现异步或同步的添加方式

 1         /// <summary>
 2         /// 添加文件方法
 3         /// </summary>
 4         /// <param name="config"></param>
 5         public void Create(IFileConfig config)
 6         {
 7             lock (_lockObject)
 8             {
 9                 //获得建立文件配置的对象
10                 var createFileConfig = config as CreateFileConfig;
11                 //假设建立完文件后写入一段话,实际项目中无需这么作,这里只是演示
12                 char[] insertContent = "HellowWord".ToCharArray();
13                 if (createFileConfig == null)
14                 {
15                     return;
16                 }
17                 //转化成byte[]
18                 byte[] byteArrayContent = Encoding.Default.GetBytes(insertContent, 0, insertContent.Length);
19                 //根据传入的配置文件来决定是否同步或者异步实例化Stream对象
20                 FileStream stream = createFileConfig.IsAsync
21                     ? new FileStream(createFileConfig.CreateUrl, FileMode.Create, FileAccess.ReadWrite, FileShare.None,
22                         4096, true)
23                     : new FileStream(createFileConfig.CreateUrl, FileMode.Create);
24                 using (stream)
25                 {
26                     //若是不注释下面代码会抛出异常,google上提示是WriteTimeOut只支持网络流
27                     //stream.WriteTimeout=READ_OR_WRITE_TIMEOUT;
28                     //若是流是同步而且可写
29                     if (!stream.IsAsync && stream.CanWrite)
30                     {
31                         stream.Write(byteArrayContent, 0, byteArrayContent.Length);
32                     }
33                     else if (stream.CanWrite)//异步可写
34                     {
35                         stream.BeginWrite(byteArrayContent, 0, byteArrayContent.Length, End_CreateFileCallBack, stream);
36                     }
37                 }
38             }
39         }

若是采用异步的方式则最后会进入End_CreateFileCallBack回调方法,result AsyncState 对象就是上图stream.BeginWrite()方法的最后一个参数。还有一点必须注意的是每一次使用BeginWrite()方法都要带上EndWrite()方法,Read方法也同样

 1         /// <summary>
 2         /// 异步写文件callBack方法
 3         /// </summary>
 4         /// <param name="result"></param>
 5         private void End_CreateFileCallBack(IAsyncResult result)
 6         {
 7             //从IAsyncResult对象中获得原来的FileStream
 8             var stream = result.AsyncState as FileStream;
 9             //结束异步写
10             if (stream != null)
11             {
12                 Console.WriteLine("异步建立文件地址{0}", stream.Name);
13                 stream.EndWrite(result);
14             }
15 
16             Console.ReadKey();
17         }

文件复制的方式思路比较类似,首先定义复制文件配置类,因为在异步回调中用到该配置类的属性,因此新增了文件流对象和相应的字节数组

 1         /// <summary>
 2         /// 异步读文件方法
 3         /// </summary>
 4         /// <param name="result"></param>
 5         private void End_ReadFileCallBack(IAsyncResult result)
 6         {
 7             //获得先前的配置文件
 8             var config = result.AsyncState as CopyFileConfig;
 9             //结束异步读
10             config?.OriginalFileStream.EndRead(result);
11             //异步读后当即写入新文件地址
12             if (config != null)
13             {
14                 FileStream copyStream = new FileStream(config.DestinationFileUrl, FileMode.CreateNew, FileAccess.Write, FileShare.Write, 4096, true);
15                 using (copyStream)
16                 {
17                     Console.WriteLine("异步复制原文件地址:{0}", config.OriginalFileStream.Name);
18                     Console.WriteLine("复制后的新文件地址:{0}", config.DestinationFileUrl);
19                     //调用异步写方法callBack方法为End_CreateFileCallBack,参数是copyStream
20                     copyStream.BeginWrite(config.OriginalFileBytes, 0, config.OriginalFileBytes.Length,
21                         End_CreateFileCallBack, copyStream);
22                 }
23             }
24         }

而后在FileStreamTest类中新增一个Copy方法实现文件的复制功能

 1         /// <summary>
 2         /// 复制文件
 3         /// </summary>
 4         /// <param name="config"></param>
 5         public void Copy(IFileConfig config)
 6         {
 7             lock (_lockObject)
 8             {
 9                 //获得CopyFileConfig对象
10                 var copyFileConfig = config as CopyFileConfig;
11                 if (copyFileConfig == null)
12                 {
13                     return;
14                 }
15                 //建立同步或异步流
16                 FileStream stream = copyFileConfig.IsAsync
17                     ? new FileStream(copyFileConfig.OriginalFileUrl, FileMode.Open, FileAccess.Read, FileShare.Read,
18                         4096, true)
19                     : new FileStream(copyFileConfig.OriginalFileUrl, FileMode.Open);
20                 //定义一个byte数组接收从原文件读取的byte数据
21                 byte[] originalFileBytes = new byte[stream.Length];
22                 using (stream)
23                 {
24                     //若是异步流
25                     if (stream.IsAsync)
26                     {
27                         //将该流和流独处的byte[]数据放入配置类,在callback中可使用
28                         copyFileConfig.OriginalFileStream = stream;
29                         copyFileConfig.OriginalFileBytes = originalFileBytes;
30                         if (stream.CanRead)
31                         {
32                             //异步开始读取,读取完后进入End_ReadFileCallBack方法,该方法接收copyFileConfig参数
33                             stream.BeginRead(originalFileBytes, 0, originalFileBytes.Length, End_ReadFileCallBack,
34                                 copyFileConfig);
35                         }
36                         else//不然同步读取
37                         {
38                             if (stream.CanRead)
39                             {
40                                 //读取原文件
41                                 stream.Read(originalFileBytes, 0, originalFileBytes.Length);
42                             }
43                             //定义一个写流,在新位置中建立一个文件
44                             FileStream copyStream = new FileStream(copyFileConfig.DestinationFileUrl, FileMode.CreateNew);
45                             using (copyStream)
46                             {
47                                 //将原文件的内容写进新文件
48                                 copyStream.Write(originalFileBytes, 0, originalFileBytes.Length);
49                             }
50                         }
51 
52                         Console.ReadLine();
53                     }
54                 }
55             }
56         }

最后,若是采用异步的方式,则会进入End_ReadFileCallBack回调函数进行异步读取和异步写操做

 1         /// <summary>
 2         /// 异步读文件方法
 3         /// </summary>
 4         /// <param name="result"></param>
 5         private void End_ReadFileCallBack(IAsyncResult result)
 6         {
 7             //获得先前的配置文件
 8             var config = result.AsyncState as CopyFileConfig;
 9             //结束异步读
10             config?.OriginalFileStream.EndRead(result);
11             //异步读后当即写入新文件地址
12             if (config != null)
13             {
14                 FileStream copyStream = new FileStream(config.DestinationFileUrl, FileMode.CreateNew, FileAccess.Write, FileShare.Write, 4096, true);
15                 using (copyStream)
16                 {
17                     Console.WriteLine("异步复制原文件地址:{0}", config.OriginalFileStream.Name);
18                     Console.WriteLine("复制后的新文件地址:{0}", config.DestinationFileUrl);
19                     //调用异步写方法callBack方法为End_CreateFileCallBack,参数是copyStream
20                     copyStream.BeginWrite(config.OriginalFileBytes, 0, config.OriginalFileBytes.Length,
21                         End_CreateFileCallBack, copyStream);
22                 }
23             }
24         }

最有让咱们在Main函数调用一下:

 1         static void Main(string[] args)
 2         {
 3             //文件操做测试
 4             FileStreamTest test = new FileStreamTest();
 5             //建立文件配置类
 6             CreateFileConfig createFileConfig = new CreateFileConfig
 7             {
 8                 CreateUrl = @"E:\本身的\MyTest\Word\新建的.txt",
 9                 IsAsync = true
10             };
11             //复制文件配置类
12             CopyFileConfig copyFileConfig = new CopyFileConfig
13             {
14                 OriginalFileUrl = @"E:\本身的\MyTest\Word\TextFile1.txt",
15                 DestinationFileUrl = @"E:\本身的\MyTest\Word\TextFile1-副本.txt",
16                 IsAsync = true
17             };
18             //test.Create(createFileConfig);
19             test.Copy(copyFileConfig);
20             Console.ReadKey();
21         }

输出结果:

 

 好了,FileStream的相关知识就分享到这里了。

相关文章
相关标签/搜索