自動化されたパイプラインで大量のExcelスプレッドシートのバッチを処理する場合、その内容を把握するためだけにドキュメント全体をメモリにロードすることはめったにありません。多くの場合、ファイル内に埋め込まれたメタデータ(作成者、タイトル、作成日、カスタムプロパティなど)だけで、ドキュメントのルーティング、インデックス作成、または拒否を行うのに十分です。Microsoft Officeの世界では、このメタデータはドキュメントプロパティ情報(Document Summary Information)として知られています。
Delphiで(ホストマシンにExcelがインストールされている必要がある)OLEオートメーションに依存せずにこの情報をネイティブに抽出するには、基盤となるファイル構造を直接解析する必要があります。この記事では、Excelファイルでドキュメントプロパティがどのように機能するか、および生のストリーム解析を使用してそれらを効率的に抽出する方法について説明します。
Excelメタデータストリームの理解
歴史的に、古いExcelファイル(.xls)はOLE複合ドキュメント形式(OLE Compound Document)で保存されており、ストリームとストレージを含むミニファイルシステムとして効果的に機能します。メタデータは、次の2つの特定のストリームに格納されています。
SummaryInformation: タイトル、サブジェクト、作成者、キーワード、リビジョン番号などの標準プロパティが含まれます。DocumentSummaryInformation: 会社、管理者、カスタムのユーザー定義プロパティなどの拡張プロパティが含まれます。
最新のExcelファイル(.xlsx)は、zip圧縮されたXML構造であるOffice Open XML(OOXML)形式を使用しています。ここでのメタデータは、docProps/core.xml、docProps/app.xml、およびdocProps/custom.xmlにあります。堅牢なDelphi解析コンポーネントは、開発者に統一されたAPIを公開しながら、両方の内部構造をシームレスに処理する必要があります。
DelphiでのOLE複合ドキュメントの解析
サードパーティのツールなしでレガシーな「.xls」ファイルからSummaryInformationを読み取るには、OLE構造化ストレージを解析する必要があります。MicrosoftはこれをCOMインターフェースIPropertySetStorageを通じて公開しています。以下は、Excelを起動せずに済む生のDelphiの実装です。
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」ファイルには機能しますが、(ZIPアーカイブである)最新の「.xlsx」ファイルには機能しません。さらに、クロスプラットフォーム(たとえば、FireMonkey経由でLinuxまたはmacOS)でCOM APIを使用することは不可能です。HotXLSコンポーネントの最近のアップデートでは、Delphiコードで完全にネイティブに両方の形式にわたってこれらのプロパティストリームの読み取りを分離および最適化するための専用ユニット(lxXlsSummaryなど)が導入されました。
クロスプラットフォームの例
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(ドキュメントオブジェクトモデル)のインスタンス化を避け、docProps/core.xmlまたはOLEプロパティストリームのみを解析することで、アプリケーションのフットプリントは信じられないほど小さく保たれます。ネットワーク共有全体で10,000個のExcelファイルのインデックスを作成している場合、それぞれを完全に解析しようとするとメモリがスラッシングし、何時間もかかります。専用のプロパティ抽出では、同じタスクが数秒で完了します。
さらに、ストリームをネイティブに読み取ることで、Excel.exeを呼び出すことなく、アプリケーションをバックグラウンドサービスとして、またはヘッドレスLinuxサーバー上で実行できるようになります。これは最新のスケーラブルなアーキテクチャにとって不可欠な要件です。
注: 包括的なExcel解析およびメタデータ抽出ツールは、HotXLS VCL Componentで利用できます。