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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...