FileStream相關知識分享

来源:https://www.cnblogs.com/xiaomowang/archive/2019/11/16/11770864.html
-Advertisement-
Play Games

一、如何理解FIleStream 通過前3章的學些,相信大家對於Stream已經有一定的瞭解,但是又如何去理解FileStream呢?請看下圖: 我們磁碟中的任何文件都是通過二進位數組組成,最為直觀的就是記事本了,當我們新建一個記事本時,它的大小時0KB,我們每次輸入一個數字或字母時,文件便會自動增 ...


一、如何理解FIleStream

通過前3章的學些,相信大家對於Stream已經有一定的瞭解,但是又如何去理解FileStream呢?請看下圖:

 

 我們磁碟中的任何文件都是通過二進位數組組成,最為直觀的就是記事本了,當我們新建一個記事本時,它的大小時0KB,我們每次輸入一個數字或字母時,文件便會自動增大到4KB,可見,隨著我們輸入的內容越來越多,文件也會越來越大,同理,當我們刪除文件內容時,文件也會相應的減小,對了,聰明的你肯定會問:誰將內容以怎樣的形式放到文件中去了?

好問題,還記得第一章流的概念嘛?對了,真實世界的一群魚可以通過河流往前往各個地方,FileStream也一樣,byte可以通過FileStream進行傳輸,這樣我們便能在電腦上對任何文件進行一系列操作了。

二、FileStream的重要性

FileStream顧名思義文件流,我們電腦上的文件都可以通過文件流進行操作,例如文件的複製、簡介、粘貼、刪除、本地文件上傳、下載、等許多重要的功能都離不開文件流。所以文件流不僅在本機上非常重要,在如今的網路世界上也萬萬不能缺少的,想象一下我們開啟虛擬機後,直接從本地複製一個文件到虛擬機上,時多麼的方便,如果沒有文件流,這個將難以想象。(大家別誤解,文件流通過網路流將客戶都安上傳的文件傳到伺服器端接收,然後通過文件流進行處理,下載正好相反)

三、FileStream常用構造函數介紹

1、FileStream(SafeFileHandle safeFileHandle,FileAccess fileAccess)

非托管參數SafeFileHandle簡單介紹

SafeFileHandle:是一個文件安全句柄,這樣的解釋可能大家一頭霧水,別急,大家先不要去理睬這深邃的含義,只要知道這個類型是C#非托管資源,也就是說它能夠調用非托管資源的方法,而且不屬於C#回收機制,所以我們必須使用GC手動或其他方式(Finalize或Dispose方法)進行非托管資源的回收,所以SafeFileHandle是一個默默無聞的保鏢,一直暗中保護FileStream和文件的安全,為了讓大家更好的理解這個保鏢,請看第一段代碼:

 

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後都必須將他關閉並釋放資源。

2、FileStream(string str,FileModel model)

string 參數表示文件所在的地址,FileMode是個枚舉,表示確定如何打開或創建文件 。

FileModel枚舉參數包含以下內容:

成員名稱

說明

Append

打開現有文件並查找到文件尾,或創建新文件。FileMode.Append 只能同 FileAccess.Write 一起使用。

    

Create

指定操作系統應創建新文件。如果文件已存在,它將被改寫。這要求 FileIOPermissionAccess.Write。

System.IO.FileMode.Create 等效於這樣的請求:如果文件不存在,則使用 CreateNew;否則使用 Truncate。

CreateNew

指定操作系統應創建新文件。此操作需要 FileIOPermissionAccess.Write。如果文件已存在,則將引發 IOException。

    

Open

指定操作系統應打開現有文件。打開文件的能力取決於 FileAccess   所指定的值。如果該文件不存在,

則引發 System.IO.FileNotFoundException。

    

OpenOrCreate

指定操作系統應打開文件(如果文件存在);否則,應創建新文件。如果用 FileAccess.Read   打開文件,則需要

 FileIOPermissionAccess.Read。如果文件訪問為 FileAccess.Write 或 FileAccess.ReadWrite,則需要

FileIOPermissionAccess.Write。如果文件訪問為 FileAccess.Append,則需要 FileIOPermissionAccess.Append。

    

Truncate

指定操作系統應打開現有文件。文件一旦打開,就將被截斷為零位元組大小。此操作需要 FileIOPermissionAccess.Write。

試圖從使用 Truncate 打開的文件中進行讀取將導致異常。

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

FileAccess參數也是一個枚舉,表示對該文件的操作許可權:

 

 參數ownsHandle:也就是類似於前面和大家介紹的SafeFileHandler,有2點必須註意:(1)對於指定的文件句柄,操作系統不允許所請求的access,例如:當access為Write或ReadWrite而文件句柄設置為只讀訪問的時候,會出現異常。所以ownsHandle才是老大,FileAccess的許可權應該在ownsHandle的範圍內。(2)FileStream假定它的句柄有獨占控制權,當FileStream也持有句柄時,讀取、寫入或查找可能會導致數據破壞,為了數據安全,請使用句柄前調用Flush,並避免在使用完句柄後調用Close以外的任何方法。

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

FileShare:同樣時一個枚舉類型,確定文件如何由進程共用。

Delete

允許隨後刪除文件。

Inheritable

使文件句柄可由子進程繼承。Win32 不直接支持此功能。

None

謝絕共用當前文件。文件關閉前,打開該文件的任何請求(由此進程或另一進程發出的請求)都將失敗。

Read

允許隨後打開文件讀取。如果未指定此標誌,則文件關閉前,任何打開該文件以進行讀取的請求(由此進程或另一進程發出的請求)都將失敗。但是,即使指定了此標誌,仍可能需要附加許可權才能夠訪問該文件。

ReadWrite

允許隨後打開文件讀取或寫入。如果未指定此標誌,則文件關閉前,任何打開該文件以進行讀取或寫入的請求(由此進程或另一進程發出)都將失敗。但是,即使指定了此標誌,仍可能需要附加許可權才能夠訪問該文件。

Write

允許隨後打開文件寫入。如果未指定此標誌,則文件關閉前,任何打開該文件以進行寫入的請求(由此進程或另一進過程發出的請求)都將失敗。但是,即使指定了此標誌,仍可能需要附加許可權才能夠訪問該文件。

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

Int32:這是一個緩衝區的大小,大家可以按照自己的需要定製;

Boolean async:是否非同步讀寫,告訴FileStream示例,是否採用非同步讀寫

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

FileOption:這是類似於FileStream對於我呢見操作的高級選項

四、FileStream常用屬性介紹

1、CanRead:指示FileStream是否可以讀操作

2、CanSeek:指示FileStream是否可以跟蹤查找流操作

3、IsAsync:FileStream是否同步工作還是非同步工作

4、Name:FileStream的名字,只讀屬性

5、ReadTimeout:設置讀取超時時間

6、SafeFileHandle:文件安全句柄,只讀屬性

7、Position:當前FileStream所在的流的位置

五、FileStream常用方法介紹

以下方法重寫了Stream的一些虛方法

1、IAsyncResult BeginRead 非同步讀取

2、IAsyncResult BeginWrite 非同步寫

3、void Close 關閉當前FileStream

4、void EndRead 非同步讀取結束

5、void EndWrite 非同步寫結束

6、void Flush 立刻釋放緩衝區,將數據全部導出到基礎流(文件)中

7、int Read 一般讀取

8、int ReadByte 讀取單個位元組

9、long Seek 跟蹤查找流所在的位置

10、void SetLength 設置FileStream的長度

11、void Write 一般寫

12、void WriteByte 寫入單個位元組

六、屬於FileStream獨有的方法

1、FileSecurity GetAccessControl()

這個不是很常用,FileSecurity時文件安全類,直接表達當前文件的訪問控制列表(ACL)的複合當前文件許可權的項目,ACL大家有個瞭解就行,以後會單獨和大家討論下ACL方面的知識

2、void Lock(long position,long length)

這個Lock方法和線程中的Lock關鍵字很不一樣,它能夠鎖住文件中的某一部分,非常的強悍!用了這個方法我們能夠精確鎖定住我們要鎖住的文件的部分內容

3、void SetAccessControl(FileSecurity fileSecurity)

和GetAccessControl很相似,ACL技術會再以後單獨介紹

4、void Unlock(long position,long length)

正好和lock方法相反,對於文件部分的解鎖

七、文件的新建和拷貝(主要演示文件同步和非同步操作)

首先我們嘗試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的相關知識就分享到這裡了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在上一章《初探java集合框架圖》中,我相信大部分朋友對java容器整體架構都有了初步的瞭解,那麼本章主要是想詳細的介紹以下 List 介面實現類之間的區別! ...
  • 眾所周知,Laravel 控制反轉 (IoC) / 依賴註入 (DI) 的功能非常強大。遺憾的是, 官方文檔 並沒有詳細講解它的所有功能,所以我決定自己實踐一下,並整理成文。下麵的代碼是基於 Laravel 5.4.26 的,其他版本可能會有所不同。 瞭解依賴註入 我在這裡不會詳細講解依賴註入/控制 ...
  • 數據結構之隊列、棧是很常見的數據結構,那麼其使用場景是什麼呢?以及其區別是什麼,本文將通過簡單的實例來分表模擬其實際使用 ...
  • 之前部署過BookStore項目,但是換了新電腦也想好好學習下這個示例項目,於是在新電腦上重新拉了Git上的ABP項目代碼,一編譯生成BookStore項目就報錯,可以參考 "abp示例項目BookStore編譯報錯" 。解決了這個問題之後又報錯了,截圖如下: 我一開始以為是我的資料庫鏈接語句配置錯 ...
  • Windows10開始微軟在系統顏色中添加了深色,對於UWP來說很輕鬆就能獲取到系統當前的顏色和主題色,而對於Win32應用就沒有那麼直觀了。 ...
  • 最近做了一個小網站,用到了一個使用sql server 2005的.net cms系統,但是現在我所買虛擬主機的服務商,不給虛擬主機提供sql server服務了,那就轉資料庫吧,轉啥好呢,思來想去,access?剛入行時候用了很久,簡單夠用,不過實在提不起興趣了,sqlite?嗯...還沒用過,只 ...
  • 測試結果如下圖: 測試結果整理後: 結論: 1、這幾個工具中,protobuf-net序列化和反序列化效率是最快的 2、BinaryFormatter和Newtonsoft.Json反序列化慢的比較多 3、Newtonsoft.Json序列化後的文件體積比較大 4、Newtonsoft.Json在序 ...
  • 接上一篇《DataGridView使用自定義控制項實現簡單分頁功能》,本篇使用BindingNavigator來實現簡單分頁功能。其實也只是借用了一個BindingNavigator空殼, 實現原理和代碼與上一篇幾乎一樣,實現方法如下: 1、新建一個WinForm程式,命名為BindingNaviga ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...