Technical Article

Extrahera dokumentsammanfattning från Excel-filer i Delphi

När du bearbetar stora mängder Excel-kalkylblad i en automatiserad pipeline vill du sällan läsa in hela dokumentet i minnet bara för att ta reda på vad det är. Ofta räcker den metadata som är inbäddad i filen (som författare, titel, skapandedatum och anpassade egenskaper) för att dirigera, indexera eller avvisa dokumentet. I Microsoft Office-världen kallas denna metadata för Document Summary Information (dokumentsammanfattning).

Att extrahera denna information inbyggt i Delphi utan att förlita sig på OLE-automatisering (vilket kräver att Excel är installerat på värdmaskinen) kräver att man tolkar den underliggande filstrukturen direkt. I den här artikeln kommer vi att titta på hur dokumentsammanfattningar fungerar i Excel-filer och hur man extraherar dem effektivt med hjälp av rå strömtolkning (raw stream parsing).

Förstå Excels metadata-strömmar

Historiskt sett lagras äldre Excel-filer (.xls) i OLE Compound Document-format, vilket i praktiken fungerar som mini-filsystem som innehåller strömmar och lagringar. Metadatan är inrymd i två specifika strömmar:

  • SummaryInformation: Innehåller standardegenskaper som titel, ämne, författare, nyckelord och revisionsnummer.
  • DocumentSummaryInformation: Innehåller utökade egenskaper som företag, chef och anpassade användardefinierade egenskaper.

Moderna Excel-filer (.xlsx) använder formatet Office Open XML (OOXML), som är en zippad XML-struktur. Metadatan här finns i docProps/core.xml, docProps/app.xml och docProps/custom.xml. En robust Delphi-tolkningskomponent måste sömlöst hantera båda interna strukturerna samtidigt som den exponerar ett enhetligt API för utvecklaren.

Tolka OLE Compound Documents i Delphi

För att läsa SummaryInformation från en äldre .xls-fil utan tredjepartsverktyg måste du tolka OLE Structured Storage. Microsoft exponerar detta genom COM-gränssnittet IPropertySetStorage. Här är en rå Delphi-implementering som undviker att starta 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;

Programmatisk extraktion med HotXLS

Även om Windows COM API fungerar för .xls-filer, fungerar det inte för moderna .xlsx-filer (som är ZIP-arkiv). Dessutom är det omöjligt att använda COM API över flera plattformar (t.ex. på Linux eller macOS via FireMonkey). Nya uppdateringar av HotXLS-komponenten introducerade dedikerade enheter (t.ex. lxXlsSummary) för att isolera och optimera läsningen av dessa sammanfattningsströmmar i båda formaten, helt inbyggt i Delphi-kod.

Ett exempel för flera plattformar

Genom att använda gränssnitten XlsReadDocumentSummaryInformation och XlsReadSummaryInformation kan du snabbt hämta metadatasträngarna från både .xls och .xlsx utan att behöva oroa dig för den underliggande filsystemsarkitekturen.

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;

Varför dedikerad sammanfattningsextraktion är viktigt

Den främsta fördelen med denna metod är prestanda och minnessäkerhet. Genom att undvika instansiering av den fullständiga DOM:en (Document Object Model) för arbetsboken och endast tolka docProps/core.xml eller OLE-egenskapsströmmarna, förblir applikationens fotavtryck otroligt litet. Om du indexerar 10 000 Excel-filer över en nätverksdelning kommer försök att tolka varje fil fullt ut att förstöra ditt minne och ta timmar. Dedikerad sammanfattningsextraktion slutför samma uppgift på sekunder.

Dessutom säkerställer inbyggd läsning av strömmarna att din applikation kan köras som en bakgrundstjänst eller på en headless Linux-server utan att någonsin anropa Excel.exe – ett kritiskt krav för moderna skalbara arkitekturer.

Obs: Omfattande verktyg för Excel-tolkning och metadataextraktion finns tillgängliga i HotXLS VCL Component.