자동화된 파이프라인에서 대량의 Excel 스프레드시트를 처리할 때 파일이 무엇인지 파악하기 위해 전체 문서를 메모리에 로드하는 경우는 드물 것입니다. 작성자, 제목, 생성 날짜 및 사용자 정의 속성과 같이 파일에 포함된 메타데이터만으로 문서를 라우팅, 색인화 또는 거부하는 데 충분한 경우가 많습니다. Microsoft Office 환경에서 이 메타데이터는 문서 요약 정보(Document Summary Information)로 알려져 있습니다.
호스트 머신에 Excel이 설치되어 있어야 하는 OLE 자동화에 의존하지 않고 Delphi에서 고유하게 이 정보를 추출하려면 기본 파일 구조를 직접 파싱해야 합니다. 이 기사에서는 문서 요약이 Excel 파일에서 작동하는 방식과 원시 스트림 파싱을 사용하여 효율적으로 추출하는 방법에 대해 살펴보겠습니다.
Excel 메타데이터 스트림의 이해
역사적으로 이전 Excel 파일(.xls)은 스트림과 저장소를 포함하는 미니 파일 시스템 역할을 하는 OLE 복합 문서 형식(OLE Compound Document format)으로 저장됩니다. 메타데이터는 특정 두 스트림에 저장됩니다.
SummaryInformation: 제목, 주제, 작성자, 키워드 및 리비전 번호와 같은 표준 속성을 포함합니다.DocumentSummaryInformation: 회사, 관리자 및 사용자 정의 속성과 같은 확장 속성을 포함합니다.
최신 Excel 파일(.xlsx)은 압축된 XML 구조인 OOXML(Office Open 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 아카이브)에서는 작동하지 않습니다. 또한 크로스 플랫폼(예: FireMonkey를 통한 Linux 또는 macOS)에서 COM API를 사용하는 것은 불가능합니다. 최근 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 파일을 인덱싱하는 경우 각 파일을 완전히 파싱하려고 시도하면 메모리가 과부하되어 몇 시간이 걸립니다. 전용 요약 추출은 동일한 작업을 몇 초 만에 완료합니다.
또한 스트림을 기본적으로 읽으면 애플리케이션이 Excel.exe를 호출하지 않고 백그라운드 서비스나 헤드리스 Linux 서버에서 실행될 수 있으므로, 이는 최신 확장 가능한 아키텍처의 중요한 요구 사항입니다.
참고: 포괄적인 Excel 파싱 및 메타데이터 추출 도구는 HotXLS VCL Component에서 사용할 수 있습니다.