比較 Word 文件 C# 使用 .NET 中的串流比較
簡介
如果您需要在 .NET 應用程式中 compare word documents c#,且希望保持低記憶體使用量,您來對地方了。傳統的檔案基礎比較會將整個文件載入 RAM,對於大型 Word 檔或僅有串流的雲端原生情境會迅速成為瓶頸。本教學將一步步示範如何使用 GroupDocs.Comparison 進行串流式文件比較,並提供實務範例、效能技巧與除錯建議。
快速答案
- 什麼程式庫處理串流比較? GroupDocs.Comparison for .NET。
- 我可以直接從 MemoryStream 比較 Word 檔案嗎? 是 – 只需將串流傳遞給比較器。
- 生產環境需要授權嗎? 絕對需要;有效的 GroupDocs.Comparison 授權會移除浮水印。
- 支援哪些 .NET 版本? .NET Framework 4.6.1+、.NET Core 2.0+、.NET 5/6/7。
- 是否內建 async 支援? 原生未支援,但可將呼叫包在
Task.Run中以取得基本的非同步行為。
什麼是基於串流的文件比較?
GroupDocs.Comparison 中的 Comparer 類別可從任何 Stream 實作讀取文件資料,讓比較過程不必將檔案寫入磁碟。這使其非常適合雲端儲存、大檔案處理與高併發的 Web 服務。
為什麼在 C# 中使用基於串流的比較來比較 Word 文件?
基於串流的比較透過分塊處理資料,減少記憶體壓力,而非一次載入整個檔案。GroupDocs.Comparison 支援 50+ 輸入與輸出格式——包括 DOCX、PDF、PPTX、XLSX——且能處理上百頁的文件而不耗盡伺服器 RAM。此方式亦與 Azure Blob、AWS S3 或任何以 Stream 而非實體檔案路徑提供資料的 HTTP 儲存完美契合。
先決條件
- GroupDocs.Comparison for .NET(版本 25.4.0 或更新)– 支援 50 多種格式。
- .NET Framework 4.6.1+ 或 .NET Core 2.0+(包括 .NET 5/6/7)。
- 具備 C# 支援的 IDE(Visual Studio、VS Code 或 Rider)。
- 基本的 C# 串流知識(
FileStream、MemoryStream)與using陳述式。
設定 GroupDocs.Comparison for .NET
安裝步驟
使用 NuGet 套件管理員主控台
Install-Package GroupDocs.Comparison -Version 25.4.0
Install-Package GroupDocs.Comparison -Version 25.4.0
使用 .NET CLI
dotnet add package GroupDocs.Comparison --version 25.4.0
dotnet add package GroupDocs.Comparison --version 25.4.0
專業提示: 鎖定版本號以避免在新主要版本發布時出現意外的破壞性變更。
授權設定(重要!)
GroupDocs.Comparison 需要授權才能在生產環境使用。您可以先使用免費試用、取得臨時授權以進行概念驗證,或購買正式授權以無限制部署。前往 GroupDocs Purchase 了解詳情。
基本授權初始化
var license = new GroupDocs.Comparison.License();
license.SetLicense("GroupDocs.Comparison.lic");
using GroupDocs.Comparison;
using System.IO;
// This is your foundation for all comparisons
Comparer comparer = new Comparer();
現在您已準備好從任何串流來源比較文件。
如何使用串流比較 Word 文件(C#)?
將來源與目標 Word 檔案載入為串流,傳給 Comparer,再將結果寫入輸出串流。完整流程如下圖所示。
步驟 1:準備來源、目標與輸出串流
using (var sourceStream = File.OpenRead("Original.docx"))
using (var targetStream = File.OpenRead("Revised.docx"))
using (var resultStream = File.Create("ComparisonResult.docx"))
{
// Comparison logic goes here
}
string sourceDocumentPath = Path.Combine("YOUR_DOCUMENT_DIRECTORY", "source.docx");
string targetDocumentPath = Path.Combine("YOUR_DOCUMENT_DIRECTORY", "target.docx");
string outputDirectory = Path.Combine("YOUR_OUTPUT_DIRECTORY", ".");
string outputFileName = Path.Combine(outputDirectory, "comparison_result.docx");
using (Comparer comparer = new Comparer(File.OpenRead(sourceDocumentPath)))
{
// Step 2: Add the Target Document
comparer.Add(File.OpenRead(targetDocumentPath));
// Step 3: Perform Comparison and Save Results
comparer.Compare(File.Create(outputFileName));
}
說明:
File.OpenRead為兩個 Word 檔案建立唯讀串流。File.Create開啟唯寫串流,用於儲存比較結果。using陳述式保證每個串流在區塊結束時即被釋放,防止檔案鎖定與記憶體泄漏。
步驟 2:以來源串流初始化 Comparer
var comparer = new GroupDocs.Comparison.Comparer(sourceStream);
// Example: Comparing documents from byte arrays
byte[] sourceBytes = GetDocumentFromDatabase(sourceId);
byte[] targetBytes = GetDocumentFromDatabase(targetId);
using (var sourceStream = new MemoryStream(sourceBytes))
using (var targetStream = new MemoryStream(targetBytes))
using (var outputStream = new MemoryStream())
using (var comparer = new Comparer(sourceStream))
{
comparer.Add(targetStream);
comparer.Compare(outputStream);
// Now you can work with the result in memory
byte[] resultBytes = outputStream.ToArray();
}
定義說明: Comparer 類別是 GroupDocs.Comparison 的核心元件,負責載入、分析並產生兩個或多個文件串流之間的差異。
步驟 3:加入目標串流
comparer.Add(targetStream);
// If you must reuse a stream, reset its position
stream.Position = 0;
您可以多次呼叫 Add,在一次執行中將來源與多個目標版本進行比較。
步驟 4:執行比較並寫入結果
ComparisonResult 代表比較的結果,包含差異文件與相關的中繼資料。
var result = comparer.Compare();
result.Save(resultStream);
// Good - automatic disposal
using (var stream = File.OpenRead(path))
{
// Use stream
}
// Also good - manual disposal
var stream = File.OpenRead(path);
try
{
// Use stream
}
finally
{
stream?.Dispose();
}
發生了什麼?
Compare()處理兩個串流,偵測插入、刪除與格式變更,並回傳ComparisonResult物件。Save()將帶有標示的比較文件寫入先前建立的resultStream。
進階串流處理
使用 MemoryStream(例如透過 HTTP 上傳的檔案)
當您的應用程式收到檔案上傳時,通常會取得 MemoryStream。相同的 API 可直接使用,無需修改:
var uploadedSource = new MemoryStream(await httpRequest.Form.Files[0].OpenReadStream().ReadAllBytesAsync());
var uploadedTarget = new MemoryStream(await httpRequest.Form.Files[1].OpenReadStream().ReadAllBytesAsync());
var comparer = new GroupDocs.Comparison.Comparer(uploadedSource);
comparer.Add(uploadedTarget);
var result = comparer.Compare();
await result.SaveAsync(response.Body);
if (stream.CanSeek)
{
// Safe to use Position and Length properties
}
為什麼重要: 使用 MemoryStream 可免除在磁碟上建立暫存檔的需求,提升無狀態 Web 服務與容器化環境的效能。
常見陷阱與解決方案
陷阱 #1:串流位置未重設
問題: 若串流先前已被讀取(例如驗證),其位置可能已在結尾,導致比較器讀取到零位元組。
解決方案: 在傳遞串流前重設位置:
sourceStream.Position = 0;
targetStream.Position = 0;
// Example of async file reading (though GroupDocs.Comparison doesn't support async yet)
var sourceBytes = await File.ReadAllBytesAsync(sourcePath);
using (var sourceStream = new MemoryStream(sourceBytes))
{
// Comparison logic
}
陷阱 #2:忘記釋放串流
問題: 未釋放的串流會保持檔案句柄開啟,導致「檔案正在使用」錯誤。
解決方案: 總是將串流包在 using 區塊內,或如核心實作所示明確呼叫 Dispose()。
陷阱 #3:使用不可定位的串流
問題: 某些網路串流(例如 NetworkStream)不支援定位,而比較器可能需要此功能。
解決方案: 先將不可定位的串流複製到 MemoryStream:
var seekableStream = new MemoryStream();
await nonSeekableStream.CopyToAsync(seekableStream);
seekableStream.Position = 0;
[HttpPost]
public async Task<IActionResult> CompareDocuments(IFormFile sourceFile, IFormFile targetFile)
{
using (var sourceStream = sourceFile.OpenReadStream())
using (var targetStream = targetFile.OpenReadStream())
using (var outputStream = new MemoryStream())
using (var comparer = new Comparer(sourceStream))
{
comparer.Add(targetStream);
comparer.Compare(outputStream);
return File(outputStream.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "comparison.docx");
}
}
效能最佳實踐
最佳化記憶體使用
- 緩衝區大小調整: 對於大於 50 MB 的文件,將內部緩衝區大小提升至 1 MB,以減少讀寫循環。
- 非同步 I/O: 在 ASP.NET Core 中,使用非同步檔案 API(
FileStream.OpenReadAsync)以在 I/O 時釋放執行緒。
監控資源消耗
- 效能計數器: 在比較前後追蹤
Process.PrivateMemorySize64以驗證記憶體影響。 - 效能測試: 執行
dotnet benchmark測試,比較基於檔案與基於串流的方法;在 200 頁 DOCX 文件上,串流方式通常快 20‑30%。
併發控制
- 佇列系統: 限制同時比較的數量至 CPU 核心數,以避免記憶體不足的崩潰。
- 提前釋放: 在
Compare()回傳後立即釋放來源與目標串流;結果串流可保留開啟狀態,直至寫入客戶端。
實務案例
案例 1:Web 應用程式文件審閱
一個 SaaS 平台允許使用者上傳兩個合約版本進行並排審閱。上傳的檔案以 IFormFile 物件形式取得,轉換為 MemoryStream 後即時比較,並回傳帶有變更追蹤的可下載 DOCX。
案例 2:從雲端儲存批次處理
Azure Function 監聽容器中新上傳的 Blob,將每個 Blob 以串流讀取,與另一容器中儲存的基準版本比較,最後將比較結果寫回「results」容器。
案例 3:版本控制整合
DevOps 管線從 Git 倉庫抽取 Word 檔案,將它們串流送入 GroupDocs.Comparison,產生差異報告,並將報告附加於建置產物供稽核使用。
故障排除指南
| 問題 | 可能原因 | 解決方式 |
|---|---|---|
| 「Stream 不支援讀取」 | 傳入唯寫串流(例如 File.OpenWrite) | 使用 File.OpenRead 或確保 CanRead 為 true。 |
| 「物件參考未設定為物件的執行個體」 | 比較前串流為 null 或已被釋放 | 確認串流已正確初始化,並在 Compare() 之後仍保持 using 區塊開啟。 |
| 「100 MB 以上檔案效能不佳」 | 預設緩衝區大小太小,或同時執行的任務過多 | 增大緩衝區大小、限制併發,並使用 dotMemory 進行效能分析。 |
| 「生產環境授權錯誤」 | 授權檔案遺失或路徑不正確 | 將 GroupDocs.Comparison.lic 放置於應用程式根目錄,並在啟動時盡早呼叫 SetLicense。 |
| 「串流資料損毀」 | 從雲端儲存下載時網路中斷 | 在比較前驗證串流長度與檢查碼。 |
進階設定選項
var options = new CompareOptions
{
HighlightColor = Color.Yellow,
ShowDeletedContent = true,
ShowInsertedContent = true,
StyleChangeDetection = true,
Password = "optionalPassword"
};
var comparer = new GroupDocs.Comparison.Comparer(sourceStream, options);
public async Task<byte[]> CompareCloudDocuments(string sourceUrl, string targetUrl)
{
using (var httpClient = new HttpClient())
using (var sourceStream = await httpClient.GetStreamAsync(sourceUrl))
using (var targetStream = await httpClient.GetStreamAsync(targetUrl))
using (var outputStream = new MemoryStream())
using (var comparer = new Comparer(sourceStream))
{
comparer.Add(targetStream);
comparer.Compare(outputStream);
return outputStream.ToArray();
}
}
定義說明: CompareOptions 是一個設定物件,可讓您控制視覺樣式、密碼保護以及報告的變更類型。
與流行 .NET 框架的整合
ASP.NET Core 整合
public async Task<IActionResult> Compare(IFormFile source, IFormFile target)
{
using var sourceStream = new MemoryStream();
using var targetStream = new MemoryStream();
await source.CopyToAsync(sourceStream);
await target.CopyToAsync(targetStream);
sourceStream.Position = 0;
targetStream.Position = 0;
var comparer = new GroupDocs.Comparison.Comparer(sourceStream);
comparer.Add(targetStream);
var result = comparer.Compare();
var output = new MemoryStream();
result.Save(output);
output.Position = 0;
return File(output, "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"ComparisonResult.docx");
}
public DocumentComparisonResult CompareDocumentVersions(int documentId, int version1, int version2)
{
var doc1Stream = GetDocumentVersionStream(documentId, version1);
var doc2Stream = GetDocumentVersionStream(documentId, version2);
using (doc1Stream)
using (doc2Stream)
using (var outputStream = new MemoryStream())
using (var comparer = new Comparer(doc1Stream))
{
comparer.Add(doc2Stream);
comparer.Compare(outputStream);
return new DocumentComparisonResult
{
ComparisonData = outputStream.ToArray(),
ComparedAt = DateTime.UtcNow,
SourceVersion = version1,
TargetVersion = version2
};
}
}
Windows Forms / WPF 整合
var openFileDialog = new OpenFileDialog { Filter = "Word files (*.docx)|*.docx" };
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
using var source = File.OpenRead(openFileDialog.FileName);
// Repeat for target, then compare as shown earlier
}
using (var comparer = new Comparer(sourceStream))
{
comparer.Add(targetStream);
var compareOptions = new CompareOptions
{
ShowDeletedContent = true,
ShowInsertedContent = true,
GenerateSummaryPage = true
};
comparer.Compare(outputStream, compareOptions);
}
結論
在 .NET 中使用基於串流的文件比較,可提供 記憶體效率高、雲端就緒且高效能 的方式來比較 Word 檔案。透過 GroupDocs.Comparison 的 Comparer 類別,您可以直接操作 Stream 物件,避免暫存檔,並可擴展至千級同時比較。遵循上述最佳實踐——正確釋放串流、調整緩衝區、妥善授權——即可確保生產環境的穩定實作。
資源
最後更新: 2026-05-31
測試環境: GroupDocs.Comparison 25.4.0 for .NET
作者: GroupDocs
public class DocumentComparisonService
{
public async Task<ComparisonResult> CompareDocumentsAsync(Stream source, Stream target)
{
// Your comparison logic here
// This is where the earlier examples would fit
}
}
private void CompareButton_Click(object sender, EventArgs e)
{
using (var openFileDialog = new OpenFileDialog())
{
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
using (var stream = File.OpenRead(openFileDialog.FileName))
{
// Perform comparison
}
}
}
}