C# 記憶體串流 – 從 .NET 串流載入文件

C# memory stream 載入文件在使用 GroupDocs.Annotation for .NET 時是一個顛覆性的改變。 不必將檔案寫入磁碟,您可以直接從記憶體、資料庫或雲端儲存桶中取得 PDF、Word 或 Excel 檔案,並即時進行註解。 此方法可降低 I/O 延遲、提升雲原生服務的可擴展性,並將敏感資料保留在檔案系統之外。 本指南將逐步說明每個環節——為何選擇串流、如何設定、常見陷阱以及效能優化的最佳實踐。

快速解答

  • 使用 C# memory stream 的主要好處是什麼? 它可消除磁碟 I/O,讓文件的註解處理在記憶體中快速完成。
  • 哪個 GroupDocs.Annotation 類別可載入串流? Annotator 建構函式接受任何 Stream 物件,包括 MemoryStream
  • 我可以直接從 Azure Blob Storage 載入 PDF 嗎? 可以——先將 Blob 下載至 MemoryStream,再傳遞給 Annotator
  • 從串流載入時支援哪些文件格式? 超過 30 種格式,包括 PDF、DOCX、XLSX、PPTX 以及各類影像。
  • 我可以安全載入多大的檔案到記憶體? 在一般伺服器硬體上,約 100 MB 以下的檔案是安全的;較大的檔案應改用基於檔案的載入方式。

什麼是 C# memory stream?

MemoryStream 是 .NET 的一個類別,提供以記憶體而非實體檔案作為儲存的串流。它允許您在 RAM 中完整地讀取、寫入與定位位元組資料,非常適合暫時性的文件處理,尤其結合 GroupDocs.Annotation 的串流式 API 時。由於整個負載都位於記憶體中,尋址、複製與註解等操作相較於磁碟檔案快許多,這也是它在高吞吐量雲端服務中成為首選的原因。

為何使用串流載入而非檔案載入?

當需要避免寫入暫存檔案至磁碟的開銷時,串流載入的優勢便顯現。將文件保留在 MemoryStream 中即可消除磁碟 I/O、降低延遲,並提升安全性,因為資料永遠不會觸及檔案系統。此方法對於容器化或無伺服器環境尤為重要,因為這些環境的檔案系統可能為唯讀或空間受限。此外,串流可與雲端儲存服務無縫整合,直接將 Blob 下載至記憶體並進行註解,無需中間儲存。

前置條件

  1. GroupDocs.Annotation for .NET – 從 發行頁面 下載最新套件。此函式庫相容於 .NET Framework 4.6.1+ 與 .NET Core 2.0+。
  2. C# 熟練度 – 熟悉 usingStream 以及基本的 .NET 記憶體管理概念。
  3. IDE – Visual Studio 2019+(或任何相容 .NET 的編輯器)。
  4. 測試文件 – 幾個 PDF、DOCX 與 XLSX 檔案供實驗使用。
  5. 可選的雲端憑證 – 若打算從 Azure Blob 或 AWS S3 載入,請事先備妥連接字串。

匯入命名空間

在 C# 檔案的頂部加入必要的 using 指示詞:

using System;
using System.IO;
using GroupDocs.Annotation.Models;
using GroupDocs.Annotation.Models.AnnotationModels;

這些命名空間提供 Annotator 類別、註解模型以及下列範例所需的核心串流工具。

如何從 C# memory stream 載入文件?

要從記憶體串流載入文件,首先取得檔案的原始位元組(可來自磁碟、資料庫或雲端服務),將這些位元組包裝成 MemoryStream,再將該串流傳遞給 Annotator 建構函式。此模式適用於所有支援的格式,確保文件在不觸及檔案系統的情況下即可進行註解。

步驟 1:從來源建立 MemoryStream

您可以從位元組陣列、檔案讀取或雲端下載建立 MemoryStream。以下是三種常見情境:

  • 從本機檔案: File.ReadAllBytes(path)new MemoryStream(bytes)
  • 從 Azure Blob: 透過 BlobClient.DownloadContentAsync() 將 Blob 下載為 byte[],再包裝。
  • 從資料庫: 取得 BLOB 欄位的 byte[],並傳入 MemoryStream

步驟 2:使用串流初始化 Annotator

Annotator 建構函式接受任何 Stream。取得 MemoryStream 後,直接傳入即可:

// Direct answer paragraph (40–70 words) placed after the heading as required by GEO rules.

專業提示: Annotator 不會 接管串流的所有權;您需在使用完畢後自行釋放它。

Annotator 類別是什麼?

Annotator 類別是 GroupDocs.Annotation 的核心引擎,負責載入文件、套用註解並儲存結果。所有讀寫操作皆透過此單一物件進行,使其成為任何串流式工作流程的核心。它提供 AddAnnotationSaveDispose 等方法,以管理註解的生命週期。

如何在從串流載入後加入註解?

文件載入後,您可以加入任何支援的註解類型——文字、區域、點或浮水印。API 具備流暢的語法;先建立註解物件、設定屬性,然後呼叫 annotator.AddAnnotation()AddAnnotation 方法會將註解插入記憶體中的表示,隨時可儲存回串流或檔案。

string outputPath = Path.Combine("Your Document Directory", "result" + Path.GetExtension("input.pdf"));
using (Annotator annotator = new Annotator(File.OpenRead("input.pdf")))
{

範例:新增區域註解

// Direct answer paragraph (40–70 words) placed after the heading.

此程式碼片段會在 (100, 100) 位置建立一個 100 × 100 像素的矩形突顯,背景為亮黃色 (RGB = 65535)。您可依需求自訂不透明度、邊框顏色與附加註解。

如何將已註解的文件儲存回串流?

將結果儲存至串流可讓您彈性選擇儲存位置——回寫至資料庫、Azure Blob Storage,或直接回傳給 Web API 的 HTTP 回應。使用 Annotator 實例的 Save 方法,傳入任意可寫入的 Stream(例如 MemoryStreamFileStream 或網路串流)。此方法會將完整的已註解檔案寫入提供的串流。

	AreaAnnotation area = new AreaAnnotation()
	{
		Box = new Rectangle(100, 100, 100, 100),
		BackgroundColor = 65535,
	};
	annotator.Add(area);

儲存至 MemoryStream 以供後續處理

// Direct answer paragraph (40–70 words) placed after the heading.

Save 方法接受任何可寫入的 Stream。若傳入 MemoryStream,已註解的檔案會保留在 RAM 中,您可以將其作為位元組陣列 (memoryStream.ToArray()) 回傳,或直接串流至其他服務,而無需寫入磁碟。

如何在儲存後顯示確認訊息?

即時回饋有助於開發者確認註解流程是否成功,特別是在除錯或建構 UI 驅動的應用程式時。簡單的 Console.WriteLine 呼叫會在主控台印出成功訊息,但您也可以依據主機環境改用日誌框架、UI toast 通知或 HTTP 狀態碼。

	annotator.Save(File.Create(outputPath));
}

簡易主控台確認

// Direct answer paragraph (40–70 words) placed after the heading.

您可以依據主機環境將 Console.WriteLine 替換為日誌、UI toast 訊息或 HTTP 狀態碼。

常見的串流載入情境

以下列出在實務上 C# memory stream 發揮優勢的模式。

如何從來源於資料庫的 MemoryStream 載入文件?

當文件以 BLOB 形式儲存在 SQL Server 時,先將其取回為 byte[],再包裝成 MemoryStream,最後傳入 Annotator。此方式免除暫存檔案的需求,並將資料保留在記憶體中以加速處理。

Console.WriteLine($"\nDocument saved successfully.\nCheck output in {outputPath}.");
// Direct answer paragraph (40–70 words) placed after the heading.

如何在 ASP.NET Core 控制器中處理上傳檔案而不寫入磁碟?

ASP.NET Core 的 IFormFile 代表隨 HTTP 請求傳送的檔案。它提供 OpenReadStream() 方法,可回傳 Stream。直接將該串流傳入 Annotator,即可對使用者上傳的檔案進行註解,且永不寫入磁碟。

byte[] documentBytes = GetDocumentFromDatabase(); // Your method to retrieve bytes
using (MemoryStream memoryStream = new MemoryStream(documentBytes))
using (Annotator annotator = new Annotator(memoryStream))
{
    // Add annotations and process as normal
}
// Direct answer paragraph (40–70 words) placed after the heading.

兩個範例皆示範相同的模式:取得可讀取的 Stream,必要時進行包裝,然後交給 annotator。

記憶體管理最佳實踐

使用串流時需要嚴謹的資源管理,以避免記憶體洩漏與耗盡。

  • 始終使用 using – 確保 StreamAnnotator 能確定性釋放。
  • 對於小於 100 MB 的檔案優先使用 MemoryStream – 較大的檔案可能增加 GC 壓力;對於超過 150 MB 的檔案建議改用基於檔案的載入。
  • 明智地重複使用緩衝區 – 從網路下載時,依預期負載大小配置緩衝區,以減少分配次數。
  • 避免同時寫入 – 每個註解操作應使用獨立的 Annotator 實例;跨執行緒共享同一實例可能導致內部狀態損壞。
  • 監控記憶體 – 在高吞吐量服務中,於處理前後記錄 GC.GetTotalMemory(false),以早期偵測記憶體洩漏。

疑難排解常見問題

為何會出現 “Stream is not readable” 錯誤?

當提供的 Stream 不支援讀取 (CanRead == false) 或過早關閉時,就會出現此錯誤。CanRead 表示串流是否支援讀取操作。請確保以讀取權限開啟串流,且在 Annotator 完成前保持其存活。

如何防止大型文件產生 OutOfMemoryException?

將大型 PDF(> 100 MB)載入 MemoryStream 可能耗盡記憶體。可改用基於檔案的載入 (new Annotator("path/to/file.pdf")) 或使用 BufferedStream 以分塊方式處理文件。BufferedStream 為其他串流加入緩衝層,以減少讀寫呼叫並降低記憶體壓力。

什麼原因會導致 “Invalid document format” 例外?

串流可能包含損毀的資料或不支援的檔案類型。請檢查前幾個位元組(魔術數)是否符合預期格式——例如 PDF 為 %PDF-、Office Open XML 檔案為 PK。這可確保在傳遞給 annotator 前,串流內的文件是有效的。

如何處理不可定位的串流(例如 NetworkStream)?

不可定位的串流會導致需要重新定位的操作失敗。NetworkStream 透過網路 socket 取得資料,但不支援定位。請先將輸入資料複製至 MemoryStream,再將該副本傳遞給 Annotator

效能最佳化技巧

  • 非同步 I/O – 從遠端來源下載時使用 await stream.CopyToAsync(memoryStream),以保持執行緒的回應性。
  • BufferedStream – 將較慢的來源(網路、資料庫)包裝於 BufferedStream,以減少讀取呼叫。
  • 物件池 – 從池中重複使用 MemoryStream 實例(ArrayPool<byte>.Shared),降低高吞吐量 API 的分配開銷。
  • 壓縮 – 若頻寬受限,可在傳輸前壓縮位元組陣列(GZipStream),然後在 MemoryStream 中解壓縮後再進行註解。
  • 平行處理 – 批次註解時,為每個文件建立獨立任務,但使用 SemaphoreSlim 限制同時執行數,以控制記憶體使用量。

進階串流情境

如何處理加密的串流?

先將位元組陣列解密(例如使用 AesManaged)。AesManaged 實作 AES 對稱加密演算法,產生明文位元組,接著將其載入 MemoryStream。GroupDocs.Annotation 需要未加密且可讀取的文件,因此必須在將串流傳遞給 annotator 前完成解密。

如何在註解前合併多個串流成單一文件?

將每個部分的位元組陣列串接起來,建立單一的 MemoryStream,再傳遞給 Annotator。請確保合併後的格式有效(例如合併 PDF 頁面需使用正確的 PDF 容器)。此技巧在從分散的片段組合文件時相當有用。

如何註解從遠端 URL 取得的文件?

使用 HttpClient.GetByteArrayAsync(url) 下載檔案。HttpClient 會發送 HTTP 請求並接收回應,將檔案以位元組陣列回傳。將結果包裝成 MemoryStream,然後照常進行註解。務必實作逾時與重試機制,以因應暫時的網路問題。

結論

結合 C# memory stream 與 GroupDocs.Annotation for .NET 可實現快速、安全且適合雲端的文件註解。直接從記憶體載入文件,可消除磁碟 I/O、簡化容器化環境的部署,並將敏感資料保留在檔案系統之外。請記得:

  • 使用 using 區塊以確保資源確定釋放。
  • 對於約 100 MB 以下的檔案採用串流載入;較大的資產則改用檔案載入。
  • 在傳遞給 Annotator 前驗證串流的可讀性與可定位性。
  • 套用上述效能技巧,以在高吞吐量情境下保持低延遲。

遵循這些做法,您即可構建從單一使用者桌面應用程式到多租戶 SaaS 平台皆能擴展的穩健註解服務。

常見問答

Q: GroupDocs.Annotation for .NET 在從串流載入時是否相容所有文件格式?
A: 是的。此函式庫支援 30+ 輸入格式(PDF、DOCX、XLSX、PPTX、影像等),不論是從檔案路徑或串流載入皆可。

Q: 在準備串流以供註解時,我能使用 async/await 嗎?
A: 雖然 Annotator 建構函式本身是同步的,但您可以在建立 annotator 前,使用非同步方式下載或讀取來源資料(例如使用 HttpClient 或 Azure SDK)。

Q: 我應該將多大的文件載入記憶體串流?
A: 為確保穩定性,於一般伺服器硬體上建議將串流大小維持在 100 MB 以下。較大的檔案建議改用基於檔案的載入,以避免過度佔用 RAM。

Q: 如果串流已被讀取,如何重設其位置?
A: 在傳遞給 Annotator 前,若串流支援定位(CanSeek == true),呼叫 stream.Seek(0, SeekOrigin.Begin) 即可。

Q: GroupDocs.Annotation 會自動釋放我傳入的串流嗎?
A: 不會。您仍需自行負責釋放串流。可將其包在 using 陳述式中,或在完成儲存已註解文件後手動呼叫 Dispose()

最後更新: 2026-07-06
測試環境: GroupDocs.Annotation 23.12 for .NET
作者: GroupDocs

// In your controller or service method
using (var uploadStream = uploadedFile.OpenReadStream())
using (Annotator annotator = new Annotator(uploadStream))
{
    // Process the uploaded document directly
}

相關教學