Někdy je jediná otázka, na kterou musí příjmová rutina odpovědět, strukturální: má tento sešit list nazvaný „Mapping", nebo kolik záložek nese. Odpovědět na to voláním Open je ten drahý způsob. Plné otevření nafoukne tabulku sdílených řetězců, dekóduje každý záznam stylu a projde buňky každého listu, protože nemá jak vědět, že jste chtěli jen obsah. U velkého souboru to znamená stovky megabajtů alokací a několik sekund CPU strávených čtením seznamu, který zabírá pár kilobajtů. HotXLS, nativní knihovna tabulkových procesorů od losLab pro Delphi, vám tento seznam dá samostatně: GetSheetNames vrátí názvy listů, v pořadí sešitu, aniž by zhmotnil jedinou buňku
Proč je čtení katalogu levné
Oba formáty tabulkových procesorů umísťují svůj obsah blízko začátku, což je to, co dělá výpisové volání rychlým, ne chytrým. Balíček OOXML drží katalog listů v xl/workbook.xml, části, která zůstává malá bez ohledu na to, jestli sešit drží deset řádků, nebo deset milionů. BIFF8 .xls ukládá své záznamy BoundSheet na začátku globálního proudu sešitu, před jakýmikoli daty buněk. Práce, které se výpisové volání vyhne, tedy není zaokrouhlovací chyba proti plnému otevření. Je to většina souboru. Čtení katalogu stojí stejnou hrstku kilobajtů bez ohledu na počet řádků, zatímco plné otevření škáluje s daty, a u víceremegabajtového sešitu se tato mezera táhne přes několik řádů velikosti, jak v dotčených bajtech, tak v alokované paměti
Tato plochá cena je vlastnost, kolem které se vyplatí navrhovat. Příjmová brána postavená na GetSheetNames se chová stejně na souboru o 200 řádcích i na 200MB souboru, takže nejpomalejší soubor v dávce už neurčuje tempo rozhodování o tom, jestli vůbec stojí za zpracování
Jedno volání napříč .xls, .xlsx a šablonovými formáty
Na fasádě XLS čte TXLSWorkbook.GetSheetNames víc než jen .xls. Přijímá také zip formáty .xlsx, .xlsm, .xltx a .xltm, přičemž z archivu vytáhne pouze workbook.xml. Pro skutečný vstup .xls skenuje záznamy BoundSheet a zastaví se na prvním záznamu EOF globálního podproudu, takže velký binární soubor pořád stojí jen svých úvodních pár kilobajtů. Fasáda XLSX nese záruku, která je pro dlouho běžící servisní kód důležitější, než se na první pohled zdá: TXLSXWorkbook.GetSheetNames instanci sešitu ani neresetuje, ani nenaplní, takže instance, která už drží otevřený dokument, může prozkoumat jiné soubory, aniž by narušila ten, který drží v ruce. GetODSSheetNames uplatňuje stejný přístup na balíčky OpenDocument, a každé z těchto volání má přetížení pro stream, které umožňuje prozkoumat nahraný soubor, jenž nikdy nepřistane na disku
var
Book: TXLSXWorkbook;
Names: TStringList;
I: Integer;
begin
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
if Book.GetSheetNames('upload-7f3a.xlsx', Names) <= 0 then
raise Exception.Create('unreadable workbook package');
if Names.IndexOf('Mapping') < 0 then
raise Exception.Create('required Mapping sheet is missing');
for I := 0 to Names.Count - 1 do
Writeln(Format('sheet %d: %s', [I, Names[I]]));
finally
Book.Free;
Names.Free;
end;
end;
Stejné volání se hodí i pro dobrý desktopový importní dialog. Vypište listy, nechte uživatele jeden vybrat a zaplaťte za plné otevření až po tom, co je volba učiněná. U sešitu s padesáti listy je rozdíl vidět: picker, který se objeví okamžitě, oproti tomu, který se zasekne, zatímco se za ním načítá celý soubor
Soubory .xlsm s podporou maker a šablonové formáty se vypisují úplně stejně jako obyčejný .xlsx, protože katalog sedí ve stejném workbook.xml bez ohledu na to, jestli se v balíčku veze vbaProject.bin. Příjmová pipeline tak může vyjmenovat listy makrového sešitu kvůli směrování, aniž by se kdy dotkla nákladu maker a aniž by udělala cokoli, co by je spustilo, a rozhodnutí o politice maker nechá na fázi, která soubor skutečně otevře
Čtení návratové hodnoty bez sebeklamu
Konvence návratových hodnot nejsou v HotXLS jednotné. Některá volání vrací při úspěchu 1, jiná vrací počet, takže pro výpisové funkce je jediná kontrola, která obstojí, brát jakoukoli hodnotu rovnou nule nebo nižší jako selhání, se seznamem řetězců vyprázdněným. Odolejte pokušení číst prázdný seznam jako „sešit bez listů". Jak ECMA-376, tak specifikace BIFF8 vyžadují v platném sešitu alespoň jeden list, takže nula názvů vždy znamená, že čtení selhalo, nikdy že je soubor legitimně prázdný
Neúspěšný výpis je sám o sobě signál, který stojí za uchování. Soubor .xlsx, u kterého volání selže, je jedna z několika konkrétních věcí: uříznutý, ve skutečnosti vůbec ne balíček OOXML (nesprávně označené exporty CSV z jiných systémů se tu objevují neustále), nebo šifrovaný kontejner. Rozlišit je je úkol další kontroly. Zalogování prvních bajtů odmítnutého souboru spolu se selháním obvykle promění vlákno podpory v jedinou zprávu
Detekce šifrovaných kontejnerů před směrováním
Šifrovaný .xlsx není zip. Je to složený soubor OLE obalující proudy EncryptionInfo a EncryptedPackage, takže GetSheetNames do něj nevidí a vrátí selhání jako u jakéhokoli jiného nečitelného souboru. CanReadEncrypted testuje právě tento tvar kontejneru, což umožňuje příjmu záměrně nasměrovat šifrovaný soubor místo polknutí obecné chyby čtení někde hluboko ve workeru:
type
TIntakeRoute = (irNormal, irNeedsPassword, irUnreadable);
function ClassifyUpload(const FileName: string; Names: TStrings): TIntakeRoute;
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
// Šifrovaný OOXML je kontejner OLE, ne zip: zkontroluj nejdřív,
// protože výpisová volání do něj nevidí.
if Book.CanReadEncrypted(FileName) then
Exit(irNeedsPassword);
if SameText(ExtractFileExt(FileName), '.ods') then
begin
if Book.GetODSSheetNames(FileName, Names) <= 0 then
Exit(irUnreadable);
end
else if Book.GetSheetNames(FileName, Names) <= 0 then
Exit(irUnreadable);
Result := irNormal;
finally
Book.Free;
end;
end;
Šifrování je místo, kde je HotXLS záměrně asymetrický, a směrování to musí respektovat. Starší šifrování .xls (RC4, RC4 CryptoAPI, XOR) je čitelné: TXLSWorkbook.Open(FileName, Password) dešifruje pomocí uloženého hesla, a tyto soubory mohou zůstat na automatizované cestě. Šifrované balíčky OOXML jdou opačným směrem. HotXLS umí jeden zapsat pomocí SaveAsEncrypted, ale nedokáže jej zpětně přečíst. OpenEncrypted vyvolá EXlsxEncryptionNotImplemented, když dostane šifrovaný balíček, a proto poctivý návrh příjmu posílá šifrovaný .xlsx člověku s Excelem a heslem chráněný .xls nechává v kódu
Pro dávkovou práci si tento klasifikátor své místo zaslouží tím, že proběhne přes celý příchozí adresář dřív, než jakýkoli worker začne skutečné zpracování, protože každá sonda stojí zhruba jedno otevření souboru a pár kilobajtů čtení. Přesun této práce dopředu mění režim selhání, na kterém provozu skutečně záleží. Místo toho, aby úloha ve 3 ráno zemřela na souboru 412 z 600, dostanete 412 souborů zařazených do fronty a 5 odmítnutých při příjmu, každý s přiloženým důvodem. Stejná volání knihovny, mnohem lepší provozní příběh
Otázky, na které výpisové volání nedokáže odpovědět
Názvy a pořadí jsou celé to, co dostanete. Výpisová volání neříkají nic o viditelnosti, takže skryté i velmi skryté listy přijdou v seznamu a vypadají jako kterýkoli jiný. Nehlásí rozměry použitého rozsahu, počty buněk ani vlastnosti dokumentu. Část docProps/core.xml je také malá, ale dnes neexistuje sonda jen pro vlastnosti, takže metadata autora a titulku pořád stojí plné Open. Čistý způsob, jak s tím žít, je nechat levná fakta směrovat každý soubor a ty drahé si šetřit na soubory, které směrováním projdou. U souborů, které skutečně postoupí do hlubokého čtení, běží čtecí sken velkého .xls znatelně rychleji s _DisableGraphics := True, který přeskočí parsování OfficeArt. Jen z takové instance nikdy neukládejte: kreslicí vrstva, kterou přeskočila, v modelu chybí, a uložení by ji ze souboru vyřadilo
Soubory, které projdou tříděním, obvykle míří do hlubší analýzy. Pracoviště pro audit a konverzi sešitů pokrývá počítadla po listech, která se vyplatí sbírat, jakmile je plné otevření oprávněné, a průvodce výkonem velkých sešitů pokrývá udržení tohoto plného otevření rychlé
HotXLS je nativní knihovna tabulkových procesorů v Object Pascalu pro Delphi a C++Builder; kompletní povrch API, včetně zde uvedených inspekčních volání, je zdokumentovaný na stránce produktu HotXLS Delphi Component