Porównywanie dokumentów Word C# przy użyciu porównania strumieniowego w .NET

Wprowadzenie

Jeśli potrzebujesz compare word documents c# w aplikacji .NET, jednocześnie utrzymując niskie zużycie pamięci, jesteś we właściwym miejscu. Tradycyjne porównanie oparte na plikach wymusza wczytanie całego dokumentu do RAM, co szybko staje się wąskim gardłem przy dużych plikach Word lub scenariuszach natywnych dla chmury, gdzie masz tylko strumień. Ten samouczek pokazuje krok po kroku, jak wykonać porównanie dokumentów oparte na strumieniu przy użyciu GroupDocs.Comparison, wraz z praktycznymi przykładami, wskazówkami dotyczącymi wydajności i poradami rozwiązywania problemów.

Szybkie odpowiedzi

  • Jaką bibliotekę obsługuje porównanie strumieniowe? GroupDocs.Comparison for .NET.
  • Czy mogę porównać pliki Word bezpośrednio z MemoryStream? Tak – po prostu przekaż strumień do porównywacza.
  • Czy potrzebuję licencji do produkcji? Zdecydowanie; ważna licencja GroupDocs.Comparison usuwa znaki wodne.
  • Jakie wersje .NET są obsługiwane? .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5/6/7.
  • Czy obsługa async jest wbudowana? Nie natywnie, ale możesz owinąć wywołania w Task.Run dla podstawowego zachowania async.

Czym jest porównanie dokumentów oparte na strumieniu?

Klasa Comparer w GroupDocs.Comparison odczytuje dane dokumentu z dowolnej implementacji Stream, umożliwiając porównanie bez zapisywania pliku na dysk. Dzięki temu jest idealna dla przechowywania w chmurze, przetwarzania dużych plików i usług internetowych o wysokiej współbieżności.

Dlaczego używać porównania opartego na strumieniu do porównywania dokumentów Word C#?

Porównanie oparte na strumieniu zmniejsza obciążenie pamięci, przetwarzając dane w fragmentach zamiast ładować cały plik. GroupDocs.Comparison obsługuje ponad 50 formatów wejściowych i wyjściowych — w tym DOCX, PDF, PPTX i XLSX — i może obsługiwać dokumenty wielokrotnie setek stron bez wyczerpania pamięci RAM serwera. Podejście to doskonale współgra z Azure Blob, AWS S3 lub dowolnym przechowywaniem opartym na HTTP, gdzie otrzymujesz Stream zamiast fizycznej ścieżki pliku.

Wymagania wstępne

  • GroupDocs.Comparison for .NET (Version 25.4.0 lub nowsza) – obsługuje ponad 50 formatów.
  • .NET Framework 4.6.1+ lub .NET Core 2.0+ (w tym .NET 5/6/7).
  • IDE z obsługą C# (Visual Studio, VS Code lub Rider).
  • Podstawowa znajomość strumieni C# (FileStream, MemoryStream) oraz instrukcji using.

Konfiguracja GroupDocs.Comparison dla .NET

Kroki instalacji

Użycie konsoli NuGet Package Manager

Install-Package GroupDocs.Comparison -Version 25.4.0
Install-Package GroupDocs.Comparison -Version 25.4.0

Użycie .NET CLI

dotnet add package GroupDocs.Comparison --version 25.4.0
dotnet add package GroupDocs.Comparison --version 25.4.0

Wskazówka: Przypnij numer wersji, aby uniknąć nieoczekiwanych zmian łamiących kompatybilność, gdy pojawi się nowa główna wersja.

Konfiguracja licencji (Ważne!)

GroupDocs.Comparison wymaga licencji do użytku produkcyjnego. Możesz rozpocząć od bezpłatnej wersji próbnej, uzyskać tymczasową licencję do prac proof‑of‑concept, lub zakupić pełną licencję na nieograniczone wdrożenia. Odwiedź GroupDocs Purchase po szczegóły.

Podstawowa inicjalizacja licencji

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();

Teraz jesteś gotowy do porównywania dokumentów z dowolnego źródła strumienia.

Jak porównać dokumenty Word C# przy użyciu strumieni?

Wczytaj swoje pliki Word źródłowy i docelowy jako strumienie, przekaż je do Comparer, i zapisz wynik do strumienia wyjściowego. Pełny przepływ przedstawiono poniżej.

Krok 1: Przygotuj strumienie źródłowy, docelowy i wyjściowy

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

Wyjaśnienie:

  • File.OpenRead tworzy strumienie tylko do odczytu dla dwóch plików Word.
  • File.Create otwiera strumień tylko do zapisu, w którym zostanie zapisany wynik porównania.
  • Instrukcje using zapewniają, że każdy strumień zostanie zwolniony natychmiast po zakończeniu bloku, zapobiegając blokadom plików i wyciekom pamięci.

Krok 2: Zainicjalizuj Comparer ze strumieniem źródłowym

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

Definicja: Klasa Comparer jest podstawowym komponentem GroupDocs.Comparison, który koordynuje ładowanie, analizowanie i generowanie różnic pomiędzy dwoma lub większą liczbą strumieni dokumentów.

Krok 3: Dodaj strumień(y) docelowy(e)

comparer.Add(targetStream);
// If you must reuse a stream, reset its position
stream.Position = 0;

Możesz wywołać Add wielokrotnie, aby porównać źródło z kilkoma wersjami docelowymi w jednym uruchomieniu.

Krok 4: Wykonaj porównanie i zapisz wynik

ComparisonResult reprezentuje wynik porównania, zawierający dokument diff oraz powiązane metadane.

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

Co się tutaj dzieje?

  • Compare() przetwarza oba strumienie, wykrywa wstawienia, usunięcia i zmiany formatowania, i zwraca obiekt ComparisonResult.
  • Save() zapisuje podświetlony dokument porównania do resultStream, który utworzyłeś wcześniej.

Zaawansowane obsługiwanie strumieni

Praca z MemoryStream (np. pliki przesyłane przez HTTP)

Kiedy Twoja aplikacja otrzymuje przesłany plik, zazwyczaj otrzymujesz MemoryStream. To samo API działa bez modyfikacji:

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
}

Dlaczego to ważne: Użycie MemoryStream eliminuje potrzebę tymczasowych plików na dysku, co poprawia wydajność w bezstanowych usługach internetowych i środowiskach konteneryzowanych.

Typowe pułapki i rozwiązania

Pułapka #1: Pozycja strumienia nie została zresetowana

Problem: Jeżeli strumień został wcześniej odczytany (np. w celu walidacji), jego pozycja może znajdować się na końcu, co powoduje, że comparer odczytuje zero bajtów.
Rozwiązanie: Zresetuj pozycję przed przekazaniem strumienia:

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
}

Pułapka #2: Zapomnienie o zwolnieniu strumieni

Problem: Niezwolnione strumienie utrzymują otwarte uchwyty plików, co prowadzi do błędów „plik w użyciu”.
Rozwiązanie: Zawsze owijaj strumienie w bloki using lub wywołuj Dispose() explicite, jak pokazano w podstawowej implementacji.

Pułapka #3: Używanie strumieni nieobsługujących przeszukiwania

Problem: Niektóre strumienie sieciowe (np. NetworkStream) nie obsługują przeszukiwania, co może być wymagane przez comparer.
Rozwiązanie: Najpierw skopiuj nie‑przeszukiwalny strumień do 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");
    }
}

Najlepsze praktyki wydajności

Optymalizacja użycia pamięci

  • Dopasowanie rozmiaru bufora: Dla dokumentów większych niż 50 MB zwiększ wewnętrzny rozmiar bufora do 1 MB, aby zmniejszyć liczbę cykli odczytu‑zapisu.
  • Async I/O: W ASP.NET Core użyj asynchronicznych API plikowych (FileStream.OpenReadAsync), aby zwolnić wątki podczas operacji I/O.

Monitorowanie zużycia zasobów

  • Liczniki wydajności: Śledź Process.PrivateMemorySize64 przed i po porównaniu, aby zweryfikować wpływ na pamięć.
  • Benchmarking: Uruchom testy dotnet benchmark porównujące podejścia oparte na plikach i na strumieniach; typowe uruchomienia oparte na strumieniach są o 20‑30 % szybsze przy plikach DOCX o 200 stronach.

Kontrola współbieżności

  • System kolejkowania: Ogranicz jednoczesne porównania do liczby rdzeni CPU, aby uniknąć awarii z powodu braku pamięci.
  • Wczesne zwalnianie: Zwolnij strumienie źródłowy i docelowy natychmiast po zwróceniu Compare(); strumień wynikowy może pozostać otwarty, aż zapiszesz go do klienta.

Przykłady zastosowań w rzeczywistych scenariuszach

Przypadek użycia 1: Przegląd dokumentów w aplikacji webowej

Platforma SaaS umożliwia użytkownikom przesłanie dwóch wersji umowy do przeglądu obok siebie. Przesłane pliki przychodzą jako obiekty IFormFile, które są konwertowane na MemoryStream i natychmiast porównywane, zwracając pobieralny DOCX ze śledzonymi zmianami.

Przypadek użycia 2: Przetwarzanie wsadowe z przechowywania w chmurze

Funkcja Azure jest wyzwalana przy nowych blobach w kontenerze, odczytuje każdy blob jako strumień, porównuje go z wersją bazową przechowywaną w innym kontenerze i zapisuje wynik porównania z powrotem do kontenera „results”.

Przypadek użycia 3: Integracja z systemem kontroli wersji

Pipeline DevOps wyodrębnia pliki Word z repozytorium Git, przesyła je jako strumienie do GroupDocs.Comparison i generuje raport diff, który jest dołączany do artefaktu builda dla audytorów.

Przewodnik rozwiązywania problemów

ProblemPrawdopodobna przyczynaRozwiązanie
“Stream does not support reading”Przekazano strumień tylko do zapisu (np. File.OpenWrite)Użyj File.OpenRead lub upewnij się, że CanRead jest true.
“Object reference not set to an instance of an object”Strumień był null lub zwolniony przed porównaniemZweryfikuj inicjalizację strumienia i utrzymaj blok using otwarty aż po wywołaniu Compare().
Poor performance on 100 MB+ filesDomyślny rozmiar bufora jest zbyt mały lub zbyt wiele jednoczesnych zadańZwiększ rozmiar bufora, ogranicz współbieżność i profiluj przy pomocy dotMemory.
Licensing errors in productionBrak pliku licencji lub nieprawidłowa ścieżkaUmieść GroupDocs.Comparison.lic w katalogu głównym aplikacji i wywołaj SetLicense wcześnie podczas uruchamiania.
Corrupted stream dataPrzerwanie sieci podczas pobierania z przechowywania w chmurzeSprawdź długość i sumę kontrolną strumienia przed porównaniem.

Zaawansowane opcje konfiguracji

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

Definicja: CompareOptions jest obiektem konfiguracyjnym, który pozwala kontrolować styl wizualny, ochronę hasłem oraz które typy zmian są raportowane.

Integracja z popularnymi frameworkami .NET

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

Integracja z 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);
}

Podsumowanie

Porównanie dokumentów oparte na strumieniu w .NET zapewnia wydajne pod względem pamięci, gotowe do chmury i wysokowydajne podejście do porównywania plików Word. Korzystając z klasy Comparer GroupDocs.Comparison, możesz pracować bezpośrednio z obiektami Stream, unikać plików tymczasowych i skalować do tysięcy jednoczesnych porównań. Stosuj się do opisanych wyżej najlepszych praktyk — właściwe zwalnianie strumieni, dopasowanie bufora i licencjonowanie — aby zapewnić solidną implementację w produkcji.

Zasoby


Ostatnia aktualizacja: 2026-05-31
Testowano z: GroupDocs.Comparison 25.4.0 for .NET
Autor: 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
            }
        }
    }
}

Powiązane samouczki