首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# FileStream 分塊複製大檔案:控制記憶體佔用的最佳實務

     編輯:關於C#

為什麼要分塊複製?

  • 避免一次配置超大陣列導致 OutOfMemoryException 或進入 Large Object Heap (LOH) 造成碎片。
  • 可報告進度、支援暫停/續傳、限制頻寬。
  • .NET 內建 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;
    }
}

使用範例 (WinForms/WPF 進度條)

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; }
}

關鍵參數調校建議

參數建議值影響
bufferSize1 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,完成後比對。
  • 稀疏檔案支援:偵測全 0 區塊,僅設定目的檔案有效資料長度 (SetFileValidData P/Invoke)。

總結

使用 FileStream 搭配適當緩衝區、SequentialScan、非同步 I/O 與 IProgress<T>,即可實作記憶體友善、可報告進度、可取消的大檔案複製元件,滿足從桌面工具到伺服器端批次處理的多樣需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved