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的相關知識就分享到這裏了。

相關文章
相關標籤/搜索