Articolo tecnico

Applicare patch a un singolo foglio in un file XLSX di grandi dimensioni da Delphi

HotXLS può riscrivere un singolo foglio di lavoro all'interno di un pacchetto XLSX esistente senza analizzare o ricomprimere il resto del file. TXLSDirectWriter.BeginPatch apre un pacchetto sorgente, copia ogni voce tranne il foglio di destinazione con i propri byte compressi invariati, e permette di riscrivere quel singolo foglio tramite le normali chiamate AddSheet, AddRow e Write*. Grafici, cache delle tabelle pivot, temi, stili e stringhe condivise non vengono mai decompressi

Il workflow che questo risolve compare nel reporting e nell'aggiornamento dati. Una cartella di lavoro arriva da un team di business con tabelle pivot, filtri dati, formattazioni condizionali e un decennio di formattazione accumulata. Ogni notte un foglio di dati deve essere sostituito con numeri aggiornati. Caricare e risalvare l'intera cartella di lavoro costa minuti per file e, più importante, rischia la fedeltà su funzionalità che il motore di caricamento deve ricostruire. Il patching aggira entrambi i problemi non toccando ciò che non ha bisogno di toccare

Perché copiare byte compressi è la parte interessante?

Una voce zip copiata a livello compresso costa una copia di stream. La stessa voce fatta passare per un normale percorso di scrittura costa un'inflate in entrata e una deflate in uscita, e deflate è la metà costosa. Su una cartella di lavoro con una cache pivot di grandi dimensioni e qualche dozzina di immagini incorporate, quella differenza è la differenza tra una patch che finisce nel tempo necessario a scrivere il nuovo foglio e una che passa la maggior parte del proprio tempo a ricomprimere byte che non ha mai esaminato

HotXLS usa CopyCompressedFrom per questo, che scrive i byte compressi della voce sorgente direttamente nell'archivio di destinazione. Quando una voce non può essere copiata in questo modo, perché usa un metodo di compressione diverso o una crittografia debole, lo scrittore ricade su una copia di stream decompresso anziché fallire. Le voci marcatore di directory vengono saltate, poiché lo scrittore produce le proprie

Sostituire sul posto, o scrivere su un nuovo file

Due overload coprono le due forme che questo compito può assumere. La forma in-place prepara il risultato in un file temporaneo accanto all'originale, chiude l'handle sorgente, poi elimina e rinomina, così un crash a metà scrittura lascia intatto l'originale. La forma con destinazione esplicita lascia intatta la sorgente e può sia sostituire un foglio sia aggiungerne uno nuovo:

var
  W: TXLSDirectWriter;
begin
  W := TXLSDirectWriter.Create;
  try
    W.BeginPatch('monthly-dashboard.xlsx', 'Data');   // sul posto
    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 di inserimento accetta un percorso sorgente e uno di destinazione più InsertSheet:

  // La sorgente resta intatta; la destinazione ottiene un foglio di lavoro aggiuntivo chiamato 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'inserimento è la parte che richiede una vera chirurgia contabile. Lo scrittore analizza il registro dei fogli in xl/workbook.xml e la mappa delle relazioni che lega ogni foglio alla propria parte, poi sceglie il prossimo numero di parte libero, l'identificatore del foglio e l'identificatore di relazione. I tipi di relazione seguono le convenzioni del pacchetto sorgente, quindi applicare una patch a una cartella di lavoro ISO 29500 strict emette tipi di relazione strict e applicarla a una transitional emette tipi transitional

Cosa la patch scarta e vincola deliberatamente

La catena di calcolo viene scartata in entrambe le modalità. In modalità sostituzione le sue voci descrivono celle in un foglio che non esiste più in quella forma; in modalità inserimento lo spostamento dell'indice del foglio la invalida del tutto. Excel ricostruisce la catena al prossimo ricalcolo, quindi scartarla è corretto e non causa perdita di dati. La parte viene esclusa dalla copia, e la sua voce di relazione e l'override del content-type vengono rimossi chirurgicamente

Due semantiche di scrittura cambiano dentro una patch, ed entrambe derivano dallo stesso principio: la patch non deve disturbare le parti che non ha riscritto. Le stringhe vengono scritte in linea nel foglio anziché aggiunte alla tabella delle stringhe condivise, perché la tabella sorgente attraversa il processo intatta. E StyleIndex fa riferimento a voci nel cellXfs del pacchetto sorgente, non a una tabella di stili costruita dallo scrittore. Questo significa che si possono referenziare formati che la cartella di lavoro originale già definisce, il che è di solito esattamente ciò che vuole un aggiornamento dati, ma significa anche che bisogna sapere quale indice porta quale formato

// Dentro una patch, StyleIndex indicizza il cellXfs del pacchetto SORGENTE.
// Una data richiede un indice esplicito che vi mappi a un formato data:
W.WriteDateTime(3, EncodeDate(2026, 8, 22), DateStyleIndexFromTemplate);

// L'overload di WriteDateTime senza stile viene rifiutato in modalità patch,
// perché presuppone la tabella di stili propria dello scrittore, che una patch
// non crea mai

Sei punti di ingresso per la scrittura sono bloccati: aggiungere tabelle, grafici, immagini, commenti, nomi definiti e stili di cella sollevano tutti un'eccezione in modalità patch, con una seconda rete di sicurezza al momento della chiusura che fallisce se uno qualsiasi dei loro contatori è diverso da zero. Ognuna di quelle funzionalità richiederebbe la modifica di parti che la patch copia letteralmente, e un pacchetto modificato a metà è peggio di un'operazione rifiutata. Può essere applicata una patch a esattamente un foglio per operazione

Quando applicare una patch e quando caricare

Il patching è lo strumento giusto quando la cartella di lavoro è grande, la modifica è confinata a un foglio, e il resto del file deve sopravvivere bit per bit. È lo strumento sbagliato quando la modifica si estende su più fogli, quando servono nuova formattazione o nuovi oggetti, o quando il file è abbastanza piccolo che un normale caricamento e salvataggio non costa nulla. Per la generazione in blocco da zero, il percorso in streaming descritto in lo scrittore diretto in streaming resta la scelta migliore, e condivide la stessa API AddRow e Write*, quindi passare dall'uno all'altro è meccanico

La manipolazione a livello di foglio dentro una cartella di lavoro caricata, quando si desidera davvero l'intero modello a oggetti, è trattata in duplicare fogli di lavoro nei pacchetti XLSX. E se il motivo per cui stai considerando una patch è che l'elaborazione dell'intera cartella di lavoro è diventata lenta, le misurazioni e il comportamento della memoria in prestazioni delle cartelle di lavoro di grandi dimensioni valgono la pena di essere lette prima di scegliere un approccio

Verificare che una patch abbia davvero fatto quello che si pensa

Tre controlli individuano quasi ogni errore. Conferma che le parti che ti aspettavi sopravvivessero siano ancora nell'archivio, che xl/calcChain.xml sia sparito, e che riaprire il file tramite TXLSXWorkbook riporti il conteggio dei fogli atteso, invariato per una sostituzione e incrementato di uno per un inserimento. Rileggere il foglio con la patch applicata e confrontare alcuni valori e formule chiude il cerchio

Un dettaglio implementativo dallo sviluppo di questa funzionalità merita di essere ripetuto, perché può mordere chiunque scriva codice simile a livello zip. I nomi delle parti dei fogli di lavoro vengono confrontati per prefisso, e un errore di uno nella lunghezza del prefisso significa che il predicato non corrisponde mai, quindi una parte appena scritta collide con un nome esistente e i lettori che prendono l'ultima voce con un dato nome scelgono silenziosamente il foglio sbagliato. Se una patch sembra scambiare il contenuto di due fogli, guarda prima la corrispondenza dei nomi e poi l'XML

Il patching in-place, la scrittura in streaming e l'intero modello a oggetti della cartella di lavoro sono distribuiti nella stessa libreria per Delphi e C++Builder; l'elenco delle funzionalità si trova nella pagina del componente foglio di calcolo Delphi HotXLS