Når en eksport på 300.000 rækker sprænger sit hukommelsesbudget, får rækketallet som regel skylden. Rækketallet er som regel uskyldigt. De dyre dele af en stor projektmappe er dem, der skabes som en sideeffekt: en stilpulje, der vokser med én post pr. celle, fordi formatering blev tilføjet inde i løkken, ark-XML samlet som én kæmpe streng ved gemmetidspunktet, en million identiske formellegemer gemt ét ad gangen. HotXLS, losLabs native Delphi-bibliotek til XLS- og XLSX-filer, giver dig en specifik håndtag til hver af disse omkostninger. Ingen af dem er slået til som standard, fordi hver især ændrer en afvejning, så at vide, hvilket håndtag der matcher hvilket symptom, er den egentlige ydeevne-færdighed
Hvor en stor projektmappe bruger hukommelse
Der er to adskilte hukommelsesregimer at ræsonnere om. Under generering vokser cellemodellen i hukommelsen med hver celle, du rører: værdier, formater og formler bliver alle til objekter eller puljeposter. Under gemning gengiver standard-XLSX-stien desuden hvert regnearks XML til en wide-streng, før den komprimeres ind i zip-beholderen, så spidsforbruget er modellen plus det største arks serialiserede form. Et job, der overlever byggeløkken og derefter dør inde i SaveAs, rammer det andet regime, ikke det første, og løsningen på det ene gør intet for det andet
Filstørrelse følger en beslægtet regel: celler er kun én bidragyder, ved siden af stilarter, delte strenge, formler, billeder og kommentarer. En revisionsgennemgang med ForEachCell og optællinger fra per-ark-samlingerne fortæller dig, hvilken ressource der reelt dominerer en problemfil, før du optimerer den forkerte. Én måle-finesse: Sheet.Cells.Count på XLSX-siden rapporterer antallet af instantierede celler i det sparsomme lager, ikke arealet af det brugte område. Et ark, hvis data optager et rektangel på 1000 gange 50 med halvdelen af cellerne tomme, tæller cirka 25.000, ikke 50.000. Den skelnen betyder noget, når man sammenligner en kundes "kæmpe" fil med sine egne fixtures, fordi det brugte områdes areal og den faktiske cellebefolkning kan afvige med en størrelsesorden i sparsomme finansielle layouts
StreamingWrite retter gemmestien, ikke byggestien
At sætte TXLSXWorkbook.StreamingWrite := True skifter SaveAs til en streaming-serialiser, der skriver ark-XML direkte ind i zip-strømmen og fjerner den per-ark-strengmellemstation. Den er som standard False af hensyn til adfærdskompatibilitet, og at slå den til er en ét-linje-ændring:
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Bulk');
for R := 1 to 100000 do
begin
Sheet.Cells[R, 1].Value := R;
Sheet.Cells[R, 2].Value := 'Row ' + IntToStr(R);
Sheet.Cells[R, 3].Value := R * 1.5;
end;
Book.StreamingWrite := True; // ark-XML strømmer ind i zip-beholderen
Book.SaveAs('bulk.xlsx');
finally
Book.Free;
end;
Vær præcis om, hvad dette køber: cellemodellen, løkken bygger, optager præcis lige så meget hukommelse som før. StreamingWrite flader gemmetids-spidsen ud, hvilket er forskellen mellem et batch-job, der fuldføres, og ét, der fejler ved 95%-mærket. Hvis selve byggeløkken opbruger hukommelsen, er de håndtag, du har brug for, de næste to
Stilpuljer: tilføj én gang, genbrug indekset
XLSX-formatering i HotXLS er poolbaseret: Book.Fonts.Add(...), Fills.AddSolid(...) og Borders.Add(...) returnerer et 0-baseret puljeindeks, som celler refererer. At kalde Fonts.Add med identiske parametre inde i en løkke bliver dedupliceret, så det spilder tid frem for plads. Alignments.Add opfører sig anderledes: den returnerer et nyt objekt pr. kald, så per-celle-justeringsoprettelse vokser puljen lineært med rækketallet. Én vane dækker begge tilfælde. Opløs hvert puljeindeks én gang, uden for løkken, og tildel indekser inde i den
// løft puljeopslag ud af den hede løkke
HeaderFont := Book.Fonts.Add('Calibri', 11, True, False); // 0-baseret puljeindeks
for C := 1 to 24 do
Sheet.Cells[1, C].FontIndex := HeaderFont + 1; // celler gemmer 1-baseret; 0 = standard
+ 1'et er ikke en tastefejl, og at glemme det er den klassiske symptomgenererende fejl her: puljerne uddeler 0-baserede indekser, mens celle-side-egenskaberne behandler 0 som "standard", så hvert puljeindeks skal forskydes med én ved tildeling. Får man det galt ved udeladelse, gengives overskrifterne i det stille i projektmappens standardskrift, en defekt ingen bemærker før branding-gennemgangen
Erstat per-celle-Variant-trafik med række-callbacks
Hver Sheet.Cells[R, C].Value := X involverer et celleopslag-eller-opret plus en Variant-tildeling. Ved et par hundrede tusinde celler bliver den per-adgang-overhead målbar i profiler. HotXLS tilbyder bulk-callback-API'er på begge facader (ForEachCell og ForEachRow til læsning, WriteCells og WriteRows til skrivning), der flytter iterationen ind i motoren og giver din kode hele rækker ad gangen:
procedure TLedgerExport.FillRow(Sender: TObject;
SheetIndex, Row, FirstCol, LastCol: Integer;
var Values: Variant; var Skip: Boolean; var Cancel: Boolean);
begin
if Row > FCount then
begin
Cancel := True; // stop hele skrivningen
Exit;
end;
Values := VarArrayOf([FRows[Row - 1].Account,
FRows[Row - 1].PostedOn,
FRows[Row - 1].Amount]);
end;
// ét motorkald i stedet for hundredtusindvis af egenskabstræf
Sheet.WriteRows(1, 1, FCount, 3, FillRow);
Callback'ens Skip-flag lader en række stå urørt uden at afbryde, og Cancel afslutter operationen tidligt, hvilket er nyttigt, når kilden er en reader, hvis længde man opdager undervejs. Kombinér WriteRows til opbygningen med StreamingWrite til gemningen, og genereringsstien har intet resterende per-celle-hotspot
Læse-side-håndtag på XLS-facaden
Store gamle .xls-filer har deres eget værktøjssæt. _DisableGraphics := True før Open springer parsing af tegnelaget helt over, hvilket fremskynder indlæsning af projektmapper, der bærer års ophobede figurer og indlejrede billeder. Begrænsningen er hård: tegnelaget er derefter fraværende fra modellen, så det at gemme en sådan projektmappe skriver en fil uden dens tegninger. Reserver dette flag til ren-læsning-analysejob. SetTempDir omdirigerer BIFF-skriverens midlertidige filer, hvilket betyder noget på servere, hvor standard-temp-placeringen har en kvote eller sidder på langsom lagring. UseSharedFormulas grupperer gentagne formellegemer i delte-formel-poster, hvilket krymper filer, hvor en formelkolonne gentager sig ned gennem tres tusind rækker
Læseløkker over XLS-data har en indekseringsfælde, det er værd at flage, fordi den fordobler arbejdet, når den håndteres defensivt, og ødelægger resultater, når den overses: UsedRange rapporterer sine FirstRow-, LastRow-, FirstCol- og LastCol-grænser 0-baseret, mens Cells.Item[Row, Col] er 1-baseret. En scanning, der går gennem det brugte område, skal lægge én til hver koordinat ved celleadgangen, som i Cells.Item[Row + 1, Col + 1], ellers læser den et gitter forskudt diagonalt med én celle, hvilket i det stille dropper den sidste række og kolonne og inkluderer en fantom-første. ForEachCell-callbacken omgår uoverensstemmelsen helt, hvilket er endnu en grund til at foretrække den til hele-ark-scanninger
Sonder filer, før du indlæser dem
Den billigste store-projektmappe-operation er den, du undgår. GetSheetNames på begge facader lister en fils regneark uden at indlæse celledata. XLSX-implementeringen læser kun projektmappe-manifestet inde i zip'en og lader eksplicit projektmappe-instansen stå ufyldt, og XLS-facaden stopper scanningen ved den første understrøms-grænse. Det gør den til det rigtige pre-flight-tjek for "hvilket ark skal dette importjob målrette", og CanReadEncrypted besvarer "er dette en krypteret beholder", før et dødsdømt Open-forsøg
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
if Book.GetSheetNames('big-unknown.xlsx', Names) <= 0 then
raise Exception.Create('cannot enumerate sheets'); // fejl rydder listen
// vælg målarket, og beslut derefter, om et fuldt Open er det værd
finally
Book.Free;
Names.Free;
end;
Bemærk return-kode-konventionen: disse sonderende funktioner signalerer fejl med værdier ved eller under nul og tømmer output-listen, så test <= 0 frem for at sammenligne mod én specifik succesværdi
At tilpasse tilgangen til opgaven
For uovervågede pipelines, der genererer mange store filer i rækkefølge, runder to yderligere vaner billedet af. Projektmappeobjekter er ikke trådsikre til deling, men intet forhindrer én uafhængig projektmappe pr. worker-tråd, hvilket parallelliserer batch-konvertering rent. Og når output går til HTTP frem for disk, kombineres TStream-gemme-overloadene med StreamingWrite, så et stort svar aldrig materialiserer sig som en temp-fil. Én driftsmæssig fodnote gælder: stream-gemningen skriver fra den nuværende position uden at spole tilbage, så sæt Position := 0, før du giver strømmen til response-frameworket. Artiklen om streaming-skrivning og batch-job udvikler det server-side-mønster, og artiklen om databaseeksport viser, hvor disse håndtag passer ind i en dataset-drevet rapport
Til sidst, behold én worst-case-fixture pr. rapportfamilie, og tag tid på den i CI. Ydeevneregressioner i dokumentgenerering annoncerer sjældent sig selv. En stil tilføjet inde i en løkke eller en sonde erstattet med et fuldt Open ændrer intet funktionelt, og den natlige batch tager simpelthen fyrre minutter længere. En tidtaget test på en repræsentativ halv-million-celle-fixture forvandler den drift til en rød build i stedet for en driftshændelse
Evalueringsbuilds, demoprojekter med et bulk-genereringseksempel og den fulde API-reference er tilgængelige på siden for HotXLS-Delphi-komponenten