HotXLS peut réécrire une seule feuille de calcul à l'intérieur d'un package XLSX existant sans analyser ni recompresser le reste du fichier. TXLSDirectWriter.BeginPatch ouvre un package source, copie chaque entrée à l'exception de la feuille cible avec ses octets compressés tels quels, et vous laisse réécrire cette seule feuille via les appels ordinaires AddSheet, AddRow et Write*. Les graphiques, caches de tableaux croisés dynamiques, thèmes, styles et chaînes partagées ne sont jamais décompressés
Le flux de travail que cela résout apparaît dans le reporting et le rafraîchissement de données. Un classeur arrive d'une équipe métier avec des tableaux croisés dynamiques, des segments, des mises en forme conditionnelles et dix ans de mise en forme accumulée. Chaque nuit, une feuille de données doit être remplacée par des chiffres frais. Charger puis réenregistrer tout le classeur coûte des minutes par fichier et, plus important encore, met en péril la fidélité sur les fonctionnalités que le moteur de chargement doit reconstruire. Le patch contourne les deux problèmes en ne touchant pas ce qu'il n'a pas besoin de toucher
Pourquoi la copie d'octets compressés est-elle la partie intéressante ?
Une entrée zip copiée au niveau compressé coûte une simple copie de flux. La même entrée passant par un chemin d'écriture normal coûte une décompression à l'entrée et une recompression à la sortie, et la recompression est la moitié coûteuse. Sur un classeur avec un grand cache de tableau croisé dynamique et quelques dizaines d'images intégrées, cette différence est celle entre un patch qui se termine dans le temps qu'il faut pour écrire la nouvelle feuille et un patch qui passe l'essentiel de son temps à recompresser des octets qu'il n'a jamais examinés
HotXLS utilise CopyCompressedFrom pour cela, qui écrit directement les octets compressés de l'entrée source dans l'archive cible. Lorsqu'une entrée ne peut pas être copiée de cette façon, parce qu'elle utilise une méthode de compression différente ou un chiffrement faible, l'écrivain se rabat sur une copie de flux décompressée plutôt que d'échouer. Les entrées marqueurs de répertoire sont ignorées, puisque l'écrivain produit les siennes
Remplacer sur place, ou écrire dans un nouveau fichier
Deux surcharges couvrent les deux formes que prend cette tâche. La forme sur place prépare le résultat dans un fichier temporaire à côté de l'original, ferme le descripteur source, puis supprime et renomme, de sorte qu'un plantage en cours d'écriture laisse l'original intact. La forme à cible explicite laisse la source intacte et peut soit remplacer une feuille, soit en ajouter une nouvelle :
var
W: TXLSDirectWriter;
begin
W := TXLSDirectWriter.Create;
try
W.BeginPatch('monthly-dashboard.xlsx', 'Data'); // sur place
W.AddSheet('Data');
W.AddRow(1);
W.WriteString(1, 'Region');
W.WriteString(2, 'Revenue');
W.AddRow(2);
W.WriteString(1, 'North');
W.WriteNumber(2, 184320.55);
W.AddRow(3);
W.WriteFormula(1, '=SUM(B2:B2)');
W.Close;
finally
W.Free;
end;
end;
La variante d'insertion prend un chemin source et un chemin cible plus InsertSheet :
// La source reste intacte ; la cible reçoit une feuille supplémentaire nommée Extra
W.BeginPatch('template.xlsx', 'output.xlsx', 'Extra', True);
W.AddSheet('Extra');
W.AddRow(1);
W.WriteString(1, 'appended by the nightly job');
W.Close;
L'insertion est la partie qui nécessite une véritable chirurgie comptable. L'écrivain analyse le registre des feuilles dans xl/workbook.xml et la table de relations qui lie chaque feuille à sa partie, puis choisit le prochain numéro de partie, identifiant de feuille et identifiant de relation libres. Les types de relations suivent les conventions du package source, de sorte que patcher un classeur ISO 29500 strict émet des types de relations stricts et patcher un classeur transitoire émet des types transitoires
Ce que le patch supprime et contraint délibérément
La chaîne de calcul est abandonnée dans les deux modes. En mode remplacement, ses entrées décrivent des cellules d'une feuille qui n'existe plus sous cette forme ; en mode insertion, le décalage d'index des feuilles l'invalide purement et simplement. Excel reconstruit la chaîne au prochain recalcul, donc l'abandonner est correct et non destructeur. La partie est exclue de la copie, et son entrée de relation ainsi que sa substitution de type de contenu sont retirées chirurgicalement
Deux sémantiques d'écriture changent à l'intérieur d'un patch, et toutes deux découlent du même principe : le patch ne doit pas perturber les parties qu'il n'a pas réécrites. Les chaînes sont écrites en ligne dans la feuille plutôt qu'ajoutées à la table de chaînes partagées, car la table source traverse intacte. Et StyleIndex fait référence à des entrées du cellXfs du package source, pas à une table de styles que l'écrivain construit. Cela signifie que vous pouvez référencer des formats déjà définis par le classeur d'origine, ce qui est généralement exactement ce qu'un rafraîchissement de données souhaite, mais cela signifie aussi que vous devez savoir quel index porte quel format
// À l'intérieur d'un patch, StyleIndex indexe le cellXfs du package SOURCE.
// Une date nécessite un index explicite qui s'y mappe vers un format de date :
W.WriteDateTime(3, EncodeDate(2026, 8, 22), DateStyleIndexFromTemplate);
// La surcharge WriteDateTime sans style est rejetée en mode patch,
// car elle suppose la table de styles propre à l'écrivain, que le patch
// ne crée jamais
Six points d'entrée d'écriture sont verrouillés : ajouter des tableaux, des graphiques, des images, des commentaires, des noms définis et des styles de cellule déclenchent tous une exception en mode patch, avec un second filet de sécurité à la fermeture qui échoue si l'un de leurs compteurs est non nul. Chacune de ces fonctionnalités nécessiterait de modifier des parties que le patch copie telles quelles, et un package modifié à moitié est pire qu'une opération refusée. Exactement une feuille peut être patchée par opération
Quand patcher et quand charger
Le patch est le bon outil lorsque le classeur est volumineux, que le changement est confiné à une seule feuille, et que le reste du fichier doit survivre bit pour bit. C'est le mauvais outil lorsque le changement s'étend sur plusieurs feuilles, lorsque de nouvelles mises en forme ou de nouveaux objets sont nécessaires, ou lorsque le fichier est assez petit pour qu'un chargement et un enregistrement normaux ne coûtent rien. Pour la génération en masse à partir de zéro, le chemin de flux décrit dans l'écrivain direct en flux reste le meilleur choix, et il partage la même API AddRow et Write*, donc passer de l'un à l'autre est mécanique
La manipulation au niveau des feuilles à l'intérieur d'un classeur chargé, lorsque vous voulez vraiment le modèle objet complet, est traitée dans la duplication de feuilles de calcul dans les packages XLSX. Et si la raison pour laquelle vous envisagez un patch est que le traitement du classeur entier est devenu lent, les mesures et le comportement mémoire décrits dans la performance des grands classeurs méritent d'être lus avant de choisir une approche
Vérifier qu'un patch a bien fait ce que vous pensez
Trois vérifications attrapent presque toutes les erreurs. Confirmez que les parties dont vous attendiez la survie sont toujours dans l'archive, que xl/calcChain.xml a disparu, et que rouvrir le fichier via TXLSXWorkbook indique le nombre de feuilles attendu, inchangé pour un remplacement et incrémenté d'une unité pour une insertion. Relire la feuille patchée et comparer quelques valeurs et formules boucle la vérification
Un détail d'implémentation issu du développement de cette fonctionnalité mérite d'être répété, car il peut piéger quiconque écrit un code similaire au niveau zip. Les noms de parties de feuilles de calcul sont appariés par préfixe, et une erreur de décalage d'un cran dans la longueur du préfixe signifie que le prédicat ne correspond jamais, de sorte qu'une partie nouvellement écrite entre en collision avec un nom existant et que les lecteurs qui prennent la dernière entrée portant un nom donné choisissent silencieusement la mauvaise feuille. Si un patch semble avoir échangé le contenu de deux feuilles, examinez la correspondance des noms avant d'examiner le XML
Le patch sur place, les écritures en flux et le modèle objet complet du classeur sont livrés dans la même bibliothèque pour Delphi et C++Builder ; la liste des fonctionnalités se trouve sur la page du composant tableur Delphi HotXLS