Technical Article

Извлечение сводной информации о документе из файлов Excel в Delphi

При обработке больших пакетов электронных таблиц Excel в автоматизированном конвейере вы редко захотите загружать весь документ в память только для того, чтобы понять, что это такое. Часто метаданных, встроенных в файл (автор, название, дата создания и пользовательские свойства), достаточно для маршрутизации, индексации или отклонения документа. В мире Microsoft Office эти метаданные известны как сводная информация о документе (Document Summary Information).

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

Понимание потоков метаданных Excel

Исторически сложилось так, что старые файлы Excel (.xls) хранятся в форматах составных документов OLE (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 в Delphi

Чтобы прочитать SummaryInformation из устаревшего файла .xls без сторонних инструментов, вам необходимо выполнить синтаксический анализ структурированного хранилища OLE (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 server) без какого-либо вызова Excel.exe, что является критическим требованием для современных масштабируемых архитектур.

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