Comparer des documents Word C# avec la comparaison de flux dans .NET

Introduction

Si vous devez compare word documents c# dans une application .NET tout en maintenant une faible consommation de mémoire, vous êtes au bon endroit. La comparaison basée sur les fichiers force le chargement complet du document en RAM, ce qui devient rapidement un goulot d’étranglement pour les gros fichiers Word ou les scénarios cloud‑native où vous ne disposez que d’un flux. Ce tutoriel vous montre, étape par étape, comment effectuer une comparaison de documents basée sur les flux en utilisant GroupDocs.Comparison, avec des exemples concrets, des conseils de performance et des solutions de dépannage.

Réponses rapides

  • Quelle bibliothèque gère la comparaison de flux ? GroupDocs.Comparison for .NET.
  • Puis-je comparer des fichiers Word directement à partir d’un MemoryStream ? Oui – il suffit de passer le flux au comparateur.
  • Ai-je besoin d’une licence pour la production ? Absolument ; une licence valide de GroupDocs.Comparison supprime les filigranes.
  • Quelles versions de .NET sont prises en charge ? .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5/6/7.
  • Le support async est‑il intégré ? Pas nativement, mais vous pouvez encapsuler les appels dans Task.Run pour un comportement async de base.

Qu’est-ce que la comparaison de documents basée sur les flux ?

La classe Comparer de GroupDocs.Comparison lit les données du document à partir de n’importe quelle implémentation de Stream, permettant la comparaison sans jamais écrire le fichier sur le disque. Cela la rend idéale pour le stockage cloud, le traitement de gros fichiers et les services web à haute concurrence.

Pourquoi utiliser la comparaison basée sur les flux pour comparer des documents Word C# ?

La comparaison basée sur les flux réduit la pression sur la mémoire en traitant les données par morceaux plutôt qu’en chargeant le fichier complet. GroupDocs.Comparison prend en charge plus de 50 formats d’entrée et de sortie — y compris DOCX, PDF, PPTX et XLSX — et peut gérer des documents de plusieurs centaines de pages sans épuiser la RAM du serveur. Cette approche s’aligne également parfaitement avec Azure Blob, AWS S3 ou tout stockage basé sur HTTP où vous recevez un Stream au lieu d’un chemin de fichier physique.

Prérequis

  • GroupDocs.Comparison for .NET (Version 25.4.0 ou ultérieure) – prend en charge plus de 50 formats.
  • .NET Framework 4.6.1+ ou .NET Core 2.0+ (incluant .NET 5/6/7).
  • Un IDE avec support C# (Visual Studio, VS Code ou Rider).
  • Connaissances de base des flux C# (FileStream, MemoryStream) et des instructions using.

Configuration de GroupDocs.Comparison pour .NET

Étapes d’installation

Using NuGet Package Manager Console

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

Using .NET CLI

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

Astuce pro : Épinglez le numéro de version pour éviter les changements majeurs inattendus lorsqu’une nouvelle version majeure apparaît.

Configuration de la licence (Important !)

GroupDocs.Comparison nécessite une licence pour une utilisation en production. Vous pouvez commencer avec un essai gratuit, obtenir une licence temporaire pour un proof‑of‑concept, ou acheter une licence complète pour des déploiements illimités. Consultez Achat GroupDocs pour plus de détails.

Basic License Initialization

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

Vous êtes maintenant prêt à comparer des documents à partir de n’importe quelle source de flux.

Comment comparer des documents Word C# en utilisant des flux ?

Chargez vos fichiers Word source et cible en tant que flux, transmettez‑les au Comparer et écrivez le résultat dans un flux de sortie. Le flux complet est illustré ci‑dessous.

Étape 1 : préparer les flux source, cible et de sortie

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

Explication :

  • File.OpenRead crée des flux en lecture seule pour les deux fichiers Word.
  • File.Create ouvre un flux en écriture seule où le résultat de la comparaison sera enregistré.
  • Les instructions using garantissent que chaque flux est libéré dès la fin du bloc, évitant les verrous de fichiers et les fuites de mémoire.

Étape 2 : initialiser le Comparer avec le flux source

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

Ancre de définition : La classe Comparer est le composant central de GroupDocs.Comparison qui orchestre le chargement, l’analyse et la génération des différences entre deux flux de documents ou plus.

Étape 3 : ajouter le(s) flux cible(s)

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

Vous pouvez appeler Add plusieurs fois pour comparer la source à plusieurs versions cibles en une seule exécution.

Étape 4 : exécuter la comparaison et écrire le résultat

ComparisonResult représente le résultat d’une comparaison, contenant le document de différences et les métadonnées associées.

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

Que se passe-t-il ici ?

  • Compare() traite les deux flux, détecte les insertions, suppressions et changements de formatage, et renvoie un objet ComparisonResult.
  • Save() écrit le document de comparaison mis en évidence dans le resultStream que vous avez créé précédemment.

Gestion avancée des flux

Utilisation de MemoryStream (par ex., fichiers téléchargés via HTTP)

Lorsque votre application reçoit un téléchargement de fichier, vous obtenez généralement un MemoryStream. La même API fonctionne sans modification :

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
}

Pourquoi c’est important : L’utilisation de MemoryStream élimine le besoin de fichiers temporaires sur le disque, ce qui améliore les performances dans les services web sans état et les environnements conteneurisés.

Pièges courants et solutions

Écueil #1 : la position du flux n’est pas réinitialisée

Problème : Si un flux a été lu auparavant (par ex., pour validation), sa position peut être à la fin, ce qui fait que le comparateur lit zéro octet.

Solution : Réinitialisez la position avant de transmettre le flux :

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
}

Écueil #2 : oublier de libérer les flux

Problème : Les flux non libérés conservent les poignées de fichiers ouvertes, entraînant des erreurs « fichier en cours d’utilisation ».

Solution : Enveloppez toujours les flux dans des blocs using ou appelez explicitement Dispose(), comme illustré dans l’implémentation principale.

Écueil #3 : utilisation de flux non recherchables

Problème : Certains flux réseau (par ex., NetworkStream) ne supportent pas la recherche, ce dont le comparateur peut avoir besoin.

Solution : Copiez d’abord le flux non recherchable dans un 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");
    }
}

Meilleures pratiques de performance

Optimiser l’utilisation de la mémoire

  • Ajustement de la taille du tampon : Pour les documents supérieurs à 50 Mo, augmentez la taille du tampon interne à 1 Mo pour réduire les cycles de lecture‑écriture.
  • E/S asynchrones : Dans ASP.NET Core, utilisez les API de fichiers asynchrones (FileStream.OpenReadAsync) pour libérer les threads pendant les I/O.

Surveiller la consommation de ressources

  • Compteurs de performance : Suivez Process.PrivateMemorySize64 avant et après la comparaison pour vérifier l’impact mémoire.
  • Benchmarking : Exécutez des tests dotnet benchmark comparant les approches basées sur les fichiers et sur les flux ; les exécutions basées sur les flux sont généralement 20‑30 % plus rapides sur des fichiers DOCX de 200 pages.

Contrôle de la concurrence

  • Système de file d’attente : Limitez les comparaisons simultanées au nombre de cœurs CPU pour éviter les plantages de mémoire.
  • Libération précoce : Libérez les flux source et cible immédiatement après le retour de Compare() ; le flux de résultat peut rester ouvert jusqu’à ce que vous l’écriviez au client.

Cas d’utilisation réels

Cas d’utilisation 1 : révision de documents d’application web

Une plateforme SaaS permet aux utilisateurs de télécharger deux versions d’un contrat pour une révision côte à côte. Les fichiers téléchargés arrivent sous forme d’objets IFormFile, qui sont convertis en MemoryStream et comparés instantanément, renvoyant un DOCX téléchargeable avec les modifications suivies.

Cas d’utilisation 2 : traitement par lots depuis le stockage cloud

Une Azure Function se déclenche sur les nouveaux blobs d’un conteneur, lit chaque blob en tant que flux, le compare à une version de référence stockée dans un autre conteneur, puis écrit le résultat de la comparaison dans un conteneur « results ».

Cas d’utilisation 3 : intégration du contrôle de version

Un pipeline DevOps extrait les fichiers Word d’un dépôt Git, les transmet sous forme de flux à GroupDocs.Comparison, et génère un rapport de différences qui est joint à l’artefact de build pour les auditeurs.

Guide de dépannage

ProblèmeCause probableSolution
“Stream does not support reading”Flux en écriture seule passé (par ex., File.OpenWrite)Utilisez File.OpenRead ou assurez‑vous que CanRead est vrai.
“Object reference not set to an instance of an object”Le flux était nul ou libéré avant la comparaisonVérifiez l’initialisation du flux et maintenez le bloc using ouvert jusqu’après Compare().
Poor performance on 100 MB+ filesTaille de tampon par défaut trop petite, ou trop de tâches concurrentesAugmentez la taille du tampon, limitez la concurrence, et profilez avec dotMemory.
Licensing errors in productionFichier de licence manquant ou chemin incorrectPlacez GroupDocs.Comparison.lic à la racine de l’application et appelez SetLicense tôt au démarrage.
Corrupted stream dataInterruption réseau lors du téléchargement depuis le stockage cloudValidez la longueur du flux et la somme de contrôle avant la comparaison.

Options de configuration avancées

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

Ancre de définition : CompareOptions est un objet de configuration qui vous permet de contrôler le style visuel, la protection par mot de passe, et quels types de modifications sont signalés.

Intégration avec les frameworks .NET populaires

Intégration 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
        };
    }
}

Intégration 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);
}

Conclusion

La comparaison de documents basée sur les flux dans .NET vous offre une méthode efficace en mémoire, prête pour le cloud et haute performance pour comparer des fichiers Word. En tirant parti de la classe Comparer de GroupDocs.Comparison, vous pouvez travailler directement avec des objets Stream, éviter les fichiers temporaires et évoluer jusqu’à des milliers de comparaisons simultanées. Suivez les meilleures pratiques décrites ci‑dessus — libération correcte des flux, réglage du tampon et gestion de la licence — pour garantir une implémentation de production robuste.

Ressources


Dernière mise à jour : 2026-05-31
Testé avec : GroupDocs.Comparison 25.4.0 for .NET
Auteur : 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
            }
        }
    }
}

Tutoriels associés