在自動化管線中處理大批量的 Excel 試算表時,您很少會為了弄清楚文件內容而將整個文件載入記憶體中。通常,檔案中內嵌的詮釋資料 (metadata,如作者、標題、建立日期與自訂屬性) 就足以用來路由、索引或拒絕該文件。在 Microsoft Office 的世界中,這種詮釋資料被稱為文件摘要資訊 (Document Summary Information)。
要在 Delphi 中原生擷取這些資訊,而不依賴 OLE 自動化 (這需要在主機上安裝 Excel),需要直接解析底層的檔案結構。在本文中,我們將探討文件摘要在 Excel 檔案中是如何運作的,以及如何使用原始串流解析 (raw stream parsing) 來有效率地擷取它們。
了解 Excel 詮釋資料串流
從歷史上看,較舊的 Excel 檔案 (.xls) 儲存於 OLE 複合文件 (Compound Document) 格式中,實際上就像是包含串流與儲存空間的迷你檔案系統。詮釋資料存放在兩個特定的串流中:
SummaryInformation:包含標準屬性,如標題、主旨、作者、關鍵字與修訂版號。DocumentSummaryInformation:包含擴充屬性,如公司、經理與自訂的使用者定義屬性。
現代的 Excel 檔案 (.xlsx) 使用 Office Open XML (OOXML) 格式,這是一種壓縮的 XML 結構。這裡的詮釋資料位於 docProps/core.xml、docProps/app.xml 與 docProps/custom.xml。一個穩健的 Delphi 解析元件必須無縫處理這兩種內部結構,同時向開發人員公開統一的 API。
在 Delphi 中解析 OLE 複合文件
要在不使用第三方工具的情況下從舊版 `.xls` 檔案讀取 SummaryInformation,您需要解析 OLE 結構化儲存 (Structured Storage)。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` 檔案,但它不適用於現代的 `.xlsx` 檔案 (ZIP 壓縮檔)。此外,使用跨平台的 COM API (例如透過 FireMonkey 在 Linux 或 macOS 上執行) 是不可能的。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 (文件物件模型) 並僅解析 docProps/core.xml 或 OLE 屬性串流,您的應用程式佔用空間會保持極小。如果您要在網路共用上為 10,000 個 Excel 檔案建立索引,嘗試完整解析每個檔案將會耗盡您的記憶體並花費數小時。專屬的摘要擷取可以在幾秒鐘內完成相同的任務。
此外,原生讀取串流可確保您的應用程式能作為背景服務或在無頭 (headless) Linux 伺服器上執行,而無需呼叫 Excel.exe,這是現代可擴充架構的關鍵需求。
備註:全面的 Excel 解析與詮釋資料擷取工具已在 HotXLS VCL 元件 中提供。