System.IO.File 只有同步方法File.ReadAllText、File.WriteAllBytes 等靜態方法雖方便,但全是同步阻塞。UI 執行緒呼叫時會凍結介面,伺服器高併發時會耗盡執行緒池。雖然 FileStream 支援原生非同步 (APM/Task),但缺乏高層級的「一次性讀寫整個檔案」封裝。
FileStream 開啟非同步旗標 (FileOptions.Asynchronous)。CopyStreamToStream 遞迴式 BeginRead/EndRead → BeginWrite/EndWrite 循環。ReadAllBytesAsync、WriteAllTextAsync 等 API,內部用 MemoryStream 暫存。AsyncOperationManager 將完成回調封送回原始同步上下文 (UI 執行緒等)。
// 核心遞迴複製
public static void CopyStreamToStream(Stream source, Stream destination, Action<Exception> completed)
{
byte[] buffer = new byte[0x1000];
AsyncCallback readCallback = null;
readCallback = ar => {
try {
int read = source.EndRead(ar);
if (read > 0) {
destination.BeginWrite(buffer, 0, read, writeAr => {
try { destination.EndWrite(writeAr); source.BeginRead(buffer, 0, buffer.Length, readCallback, null); }
catch (Exception ex) { completed(ex); }
}, null);
} else completed(null);
} catch (Exception ex) { completed(ex); }
};
source.BeginRead(buffer, 0, buffer.Length, readCallback, null);
}
// 公開 API 範例
public static void ReadAllBytesAsync(string path, Action<byte[]> success, Action<Exception> failure)
{
var asyncOp = AsyncOperationManager.CreateOperation(null);
var input = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x1000, true);
var output = new MemoryStream((int)input.Length);
CopyStreamToStream(input, output, e => {
byte[] bytes = e == null ? output.GetBuffer() : null;
output.Close(); input.Close();
asyncOp.Post(_ => e != null ? failure(e) : success(bytes), null);
});
}
WebClient + file:// 協議 —— 驚人的簡潔WebClient 內部使用 WebRequest 工廠模式。WebRequest.Create("file://C:/test.txt") 會返回 FileWebRequest,它實作了檔案的非同步讀寫。WebClient 封裝了 DownloadDataAsync/UploadDataAsync 等事件式非同步模式 (EAP),並自動處理同步上下文封送。
public static void ReadAllBytesAsync(string path, Action<byte[]> success, Action<Exception> failure)
{
var wc = new WebClient();
wc.DownloadDataCompleted += (s, e) => e.Error != null ? failure(e.Error) : success(e.Result);
wc.DownloadDataAsync(new Uri("file://" + path));
}
public static void WriteAllTextAsync(string path, string contents, Action success, Action<Exception> failure)
{
var wc = new WebClient();
wc.UploadStringCompleted += (s, e) => e.Error != null ? failure(e.Error) : success();
wc.UploadStringAsync(new Uri("file://" + path), contents);
}
wc.DownloadProgressChanged += (s, e) => progressBar.Value = e.ProgressPercentage;
wc.DownloadDataAsync(new Uri(@"file://c:\largeFile.dat"));
DownloadProgressChangedEventArgs 提供 BytesReceived、TotalBytesToReceive、ProgressPercentage,完美整合 WinForms/WPF 進度條。
警告:
FileWebRequest實作有缺陷,非同步 I/O 完成時會阻塞執行緒池執行緒等待。大量並發檔案操作會耗盡執行緒池,導致嚴重效能退化甚至死鎖。
適用場景**:偶爾的單檔案操作、UI 應用載入配置、低併發場景。
禁用場景**:高併發伺服器、批量處理大量檔案、長時間運行的服務。
Task 原生非同步 (現代首選)
// .NET 4.5+ 標準做法
public static async Task<byte[]> ReadAllBytesAsync(string path)
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
{
var bytes = new byte[fs.Length];
await fs.ReadAsync(bytes, 0, bytes.Length);
return bytes;
}
}
public static async Task WriteAllTextAsync(string path, string contents)
{
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
{
var bytes = Encoding.UTF8.GetBytes(contents);
await fs.WriteAsync(bytes, 0, bytes.Length);
}
}
CancellationToken、IProgress<T>、ValueTask 等現代模式。| 方案 | 非同步模型 | 真非同步 I/O | 進度報告 | 適用 .NET 版本 | 推薦指數 |
|---|---|---|---|---|---|
| 手寫 APM | APM (Begin/End) | ✅ 完美 | 需自行實作 | 2.0+ | ⭐⭐ (教學/極致控制) |
| WebClient + file:// | EAP (事件) | ⚠️ 有缺陷 (阻塞執行緒池) | ✅ 內建 | 2.0+ | ⭐⭐ (簡單場景/快速原型) |
FileStream + Task | TAP (async/await) | ✅ 完美 | ✅ IProgress | 4.5+ | ⭐⭐⭐⭐⭐ (現代標準首選) |
| 第三方庫 | TAP | ✅ | 視庫而定 | Standard 2.0+ | ⭐⭐⭐⭐ (功能豐富) |
async/await + FileStream,目標 .NET 4.5+ / .NET Core / .NET 5+。FileOptions.Asynchronous (或 useAsync: true),否則 ReadAsync 會同步完成。FileStream.Position 分段讀寫,配合 SemaphoreSlim 限制併發度。CancellationToken,響應用戶取消或超時。WebClient 高併發檔案操作,除非確認 .NET 版本已修復底層缺陷 (Core 3.0+ 部分改善,但仍不推薦)。