Technical Article

Extrakce souhrnných informací o dokumentu ze souborů aplikace Excel v Delphi

Při zpracování velkých dávek excelových tabulek v automatizovaném procesu jen zřídka chcete načíst celý dokument do paměti jen proto, abyste zjistili, o co jde. Často jsou metadata vložená do souboru, jako je autor, název, datum vytvoření a vlastní vlastnosti, dostačující k nasměrování, indexaci nebo odmítnutí dokumentu. Ve světě Microsoft Office jsou tato metadata známá jako souhrnné informace o dokumentu (Document Summary Information).

Nativní extrakce těchto informací v Delphi bez spoléhání na automatizaci OLE (která vyžaduje instalaci Excelu na hostitelském počítači) vyžaduje přímou analýzu základní struktury souboru. V tomto článku se podíváme na to, jak fungují souhrny dokumentů v souborech Excelu a jak je efektivně extrahovat pomocí analýzy nezpracovaných (raw) streamů.

Porozumění streamům metadat aplikace Excel

Historicky jsou starší soubory aplikace Excel (.xls) uloženy ve formátech OLE Compound Document, které efektivně fungují jako mini souborové systémy obsahující streamy a úložiště. Metadata jsou uložena ve dvou specifických streamech:

  • SummaryInformation: Obsahuje standardní vlastnosti jako název, předmět, autor, klíčová slova a číslo revize.
  • DocumentSummaryInformation: Obsahuje rozšířené vlastnosti, jako je společnost, manažer a vlastní uživatelsky definované vlastnosti.

Moderní soubory aplikace Excel (.xlsx) používají formát Office Open XML (OOXML), což je komprimovaná (zipped) struktura XML. Metadata se zde nacházejí v docProps/core.xml, docProps/app.xml a docProps/custom.xml. Robustní komponenta pro analýzu v Delphi musí bez problémů zvládnout obě vnitřní struktury a zároveň vývojáři poskytnout jednotné rozhraní API.

Analýza dokumentů OLE Compound v Delphi

Pro čtení SummaryInformation ze staršího souboru `.xls` bez nástrojů třetích stran musíte analyzovat strukturované úložiště OLE. Společnost Microsoft k tomu nabízí přístup prostřednictvím rozhraní COM IPropertySetStorage. Zde je čistá implementace v Delphi, která se vyhne spuštění aplikace Excel:

uses
  System.SysUtils, System.Win.ComObj, Winapi.ActiveX, Winapi.Windows;

procedure ExtractXlsSummaryInfo(const FileName: string);
var
  Stg: IStorage;
  PropSetStg: IPropertySetStorage;
  PropStg: IPropertyStorage;
  PropSpec: TPropSpec;
  PropVariant: TPropVariant;
  Hr: HRESULT;
begin
  // Open the OLE Compound Document
  Hr := StgOpenStorage(PWideChar(WideString(FileName)), nil,
    STGM_READ or STGM_SHARE_DENY_WRITE, nil, 0, Stg);
    
  if Failed(Hr) then
    raise Exception.Create('Failed to open OLE storage. File may not be a valid .xls document.');

  // Query for the property set storage interface
  if Stg.QueryInterface(IPropertySetStorage, PropSetStg) = S_OK then
  begin
    // Open the SummaryInformation stream (FMTID_SummaryInformation)
    Hr := PropSetStg.Open(FMTID_SummaryInformation, STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg);
    if Succeeded(Hr) then
    begin
      // Read the Author property (PIDSI_AUTHOR = 4)
      PropSpec.ulKind := PRSPEC_PROPID;
      PropSpec.propid := PIDSI_AUTHOR;
      
      if PropStg.ReadMultiple(1, @PropSpec, @PropVariant) = S_OK then
      begin
        if PropVariant.vt = VT_LPSTR then
          Writeln('Author: ', string(AnsiString(PropVariant.pszVal)));
        PropVariantClear(PropVariant);
      end;
    end;
  end;
end;

Programová extrakce pomocí HotXLS

Přestože Windows COM API funguje pro soubory `.xls`, nefunguje pro moderní soubory `.xlsx` (což jsou archivy ZIP). Kromě toho je nemožné používat rozhraní COM API napříč platformami (např. v Linuxu nebo macOS prostřednictvím FireMonkey). Nedávné aktualizace komponenty HotXLS přinesly vyhrazené jednotky (např. lxXlsSummary) k izolaci a optimalizaci čtení těchto souhrnných streamů v obou formátech, a to zcela nativně v kódu Delphi.

Příklad nezávislý na platformě

Pomocí rozhraní XlsReadDocumentSummaryInformation a XlsReadSummaryInformation můžete rychle získat řetězce metadat ze souborů `.xls` i `.xlsx`, aniž byste se museli starat o základní architekturu souborového systému.

uses
  lxXlsSummary;

var
  Summary: TXlsSummaryInfo;
  ExtendedInfo: TXlsDocumentSummaryInfo;
begin
  // Extract standard summary from an OOXML format seamlessly
  Summary := XlsReadSummaryInformation('C:\Data\FinancialReport.xlsx');
  try
    Writeln('Title: ', Summary.Title);
    Writeln('Author: ', Summary.Author);
    Writeln('Creation Date: ', DateTimeToStr(Summary.CreateTime));
  finally
    Summary.Free;
  end;

  // Extract extended document summary
  ExtendedInfo := XlsReadDocumentSummaryInformation('C:\Data\FinancialReport.xlsx');
  try
    Writeln('Company: ', ExtendedInfo.Company);
    Writeln('Manager: ', ExtendedInfo.Manager);
  finally
    ExtendedInfo.Free;
  end;
end;

Proč záleží na vyhrazené extrakci souhrnů

Hlavní výhodou tohoto přístupu je výkon a bezpečnost paměti. Tím, že se vyhnete instanciaci plného DOM (Document Object Model) sešitu a analyzujete pouze docProps/core.xml nebo streamy vlastností OLE, zůstane paměťová stopa vaší aplikace neuvěřitelně malá. Pokud indexujete 10 000 souborů Excelu na síťové sdílené složce, pokus o úplnou analýzu každého z nich zahltí vaši paměť a potrvá hodiny. Vyhrazená extrakce souhrnů dokončí stejný úkol v řádu sekund.

Kromě toho nativní čtení streamů zajišťuje, že vaše aplikace může běžet jako služba na pozadí nebo na serveru Linux bez grafického rozhraní (headless), aniž by kdy vyvolala soubor Excel.exe, což je kritický požadavek pro moderní škálovatelné architektury.

Poznámka: Komplexní nástroje pro analýzu Excelu a extrakci metadat jsou k dispozici v komponentě HotXLS VCL Component.