Technical Article

Вилучення підсумкової інформації про документ з файлів Excel у Delphi

Коли ви обробляєте великі партії електронних таблиць Excel в автоматизованому конвеєрі, вам рідко хочеться завантажувати весь документ у пам'ять лише для того, щоб зрозуміти, що це таке. Часто метаданих, вбудованих у файл: автора, назви, дати створення та користувацьких властивостей, достатньо для маршрутизації, індексування або відхилення документа. У світі Microsoft Office ці метадані відомі як Підсумкова інформація про документ (Document Summary Information).

Вилучення цієї інформації нативно у Delphi без покладання на OLE-автоматизацію (яка вимагає встановленого Excel на хост-машині) вимагає безпосереднього аналізу базової структури файлу. У цій статті ми розглянемо, як працюють підсумки документів у файлах Excel і як їх ефективно вилучати за допомогою синтаксичного аналізу необроблених потоків.

Розуміння потоків метаданих Excel

Історично старі файли Excel (.xls) зберігаються у форматах OLE Compound Document, фактично діючи як міні-файлові системи, що містять потоки та сховища. Метадані розміщені у двох спеціальних потоках:

  • SummaryInformation: Містить стандартні властивості, такі як заголовок (Title), тема (Subject), автор (Author), ключові слова (Keywords) та номер ревізії (Revision Number).
  • DocumentSummaryInformation: Містить розширені властивості, такі як компанія (Company), менеджер (Manager) та власні користувацькі властивості.

Сучасні файли Excel (.xlsx) використовують формат Office Open XML (OOXML), який є заархівованою структурою XML. Метадані тут розташовані у файлах docProps/core.xml, docProps/app.xml та docProps/custom.xml. Надійний компонент синтаксичного аналізу для Delphi повинен безперешкодно обробляти обидві внутрішні структури, надаючи розробнику уніфікований API.

Синтаксичний аналіз документів OLE Compound у Delphi

Щоб прочитати SummaryInformation із застарілого файлу `.xls` без інструментів сторонніх розробників, вам потрібно проаналізувати сховище OLE Structured Storage. Microsoft надає це через COM-інтерфейс IPropertySetStorage. Ось базова реалізація на Delphi, яка дозволяє уникнути запуску 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;

Програмне вилучення за допомогою HotXLS

Хоча Windows COM API працює для файлів `.xls`, він не працює для сучасних файлів `.xlsx` (які є ZIP-архівами). Крім того, використання COM API як кросплатформного рішення (наприклад, на Linux або macOS через FireMonkey) є неможливим. Нещодавні оновлення компонента HotXLS представили спеціальні модулі (наприклад, lxXlsSummary) для ізоляції та оптимізації читання цих підсумкових потоків в обох форматах повністю нативно у коді Delphi.

Кросплатформний приклад

Використовуючи інтерфейси XlsReadDocumentSummaryInformation та XlsReadSummaryInformation, ви можете швидко отримати рядки метаданих як з файлів `.xls`, так і з файлів `.xlsx`, не турбуючись про базову архітектуру файлової системи.

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;

Чому важливе спеціалізоване вилучення підсумків

Основною перевагою цього підходу є продуктивність та безпека пам'яті. Уникаючи створення повного DOM (Document Object Model) робочої книги та аналізуючи лише docProps/core.xml або потоки властивостей OLE, слід вашого додатка залишається неймовірно малим. Якщо ви індексуєте 10 000 файлів Excel через мережевий ресурс, спроба повністю проаналізувати кожен з них виснажить вашу пам'ять і триватиме годинами. Спеціалізоване вилучення підсумків виконує те саме завдання за лічені секунди.

Крім того, нативне читання потоків гарантує, що ваш додаток зможе працювати як фонова служба або на сервері Linux без графічного інтерфейсу (headless) без жодного виклику Excel.exe, що є критичною вимогою для сучасних масштабованих архітектур.

Примітка: Комплексні інструменти для аналізу Excel та вилучення метаданих доступні у компоненті HotXLS VCL.