OutOfMemoryException 或進入 Large Object Heap (LOH) 造成碎片。File.Copy 雖快但無進度回調、無法限制緩衝區大小。using System;
using System.IO;
public static class FileCopyHelper
{
///
/// 以指定緩衝區大小分塊複製檔案,回傳複製位元組總數
///
public static long CopyInChunks(string sourcePath, string destPath, int bufferSize = 1024 * 1024,
IProgress progress = null, CancellationToken ct = default)
{
if (bufferSize <= 0) bufferSize = 1024 * 1024; // 預設 1 MB
long totalCopied = 0;
using (var src = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, FileOptions.SequentialScan))
using (var dst = new FileStream(destPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, FileOptions.SequentialScan))
{
byte[] buffer = new byte[bufferSize];
int read;
while ((read = src.Read(buffer, 0, buffer.Length)) > 0)
{
ct.ThrowIfCancellationRequested();
dst.Write(buffer, 0, read);
totalCopied += read;
progress?.Report(totalCopied);
}
}
return totalCopied;
}
///
/// 非同步版本 (適合 UI 應用避免凍結)
///
public static async Task CopyInChunksAsync(string sourcePath, string destPath, int bufferSize = 1024 * 1024,
IProgress progress = null, CancellationToken ct = default)
{
if (bufferSize <= 0) bufferSize = 1024 * 1024;
long totalCopied = 0;
using (var src = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, FileOptions.SequentialScan | FileOptions.Asynchronous))
using (var dst = new FileStream(destPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, FileOptions.SequentialScan | FileOptions.Asynchronous))
{
byte[] buffer = new byte[bufferSize];
int read;
while ((read = await src.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
{
await dst.WriteAsync(buffer, 0, read, ct);
totalCopied += read;
progress?.Report(totalCopied);
}
}
return totalCopied;
}
}
private async void btnCopy_Click(object sender, EventArgs e)
{
btnCopy.Enabled = false;
var progress = new Progress(bytes =>
{
lblProgress.Text = $"已複製 {bytes / 1024 / 1024} MB";
progressBar.Value = (int)(bytes * 100 / new FileInfo(txtSource.Text).Length);
});
try
{
await FileCopyHelper.CopyInChunksAsync(txtSource.Text, txtDest.Text, 4 * 1024 * 1024, progress);
MessageBox.Show("複製完成");
}
catch (OperationCanceledException)
{
MessageBox.Show("已取消");
}
catch (Exception ex)
{
MessageBox.Show($"錯誤: {ex.Message}");
}
finally { btnCopy.Enabled = true; }
}
| 參數 | 建議值 | 影響 |
|---|---|---|
bufferSize | 1 MB ~ 8 MB | 太小增加系統呼叫開銷;太大佔用 LOH。SSD 建議 4-8 MB;HDD/網路磁碟 1-2 MB。 |
FileOptions.SequentialScan | 必加 | 提示 OS 關閉預讀快取,避免污染系統檔案快取。 |
FileOptions.Asynchronous | 非同步版必加 | 啟用 I/O 完成埠 (IOCP),釋放執行緒池執行緒。 |
FileShare.Read | 來源檔 | 允許其他程序同時讀取來源檔。 |
src.Seek(offset, SeekOrigin.Begin)、dst.Seek(offset, SeekOrigin.Begin)。Task.Delay 節流。SHA256 或 CRC32,完成後比對。SetFileValidData P/Invoke)。使用 FileStream 搭配適當緩衝區、SequentialScan、非同步 I/O 與 IProgress<T>,即可實作記憶體友善、可報告進度、可取消的大檔案複製元件,滿足從桌面工具到伺服器端批次處理的多樣需求。