Technical Article

Настройване на свойства на Excel документи и метаданни на работни книги в Delphi с HotXLS

Една електронна таблица носи два слоя идентичност. Има мрежа от клетки и има метаданни на документа, които вървят заедно с нея: заглавие, автор, компания, ключови думи, клейма за време. Excel никога не показва този втори слой в мрежата, но това е слоят, който Windows Search индексира, този, който SharePoint чете, за да озаглави документ, и този, по който системата за управление на записи класифицира файловете. Когато генерирана работна книга наследи своя автор и заглавие от шаблона, от който е изградена, всяка последваща система записва дизайнера на шаблона като автор на четири хиляди клиентски отчети. Метаданните не са верни никъде, а се справят с тях навсякъде.

HotXLS представя този слой като обикновени свойства на ниво работна книга и в двата си двигателя: BIFF интерфейса за .xls и OOXML интерфейса за .xlsx. Четете поле след отваряне на файл и пишете поле преди записване. Библиотеката решава в кой физически контейнер попада стойността. Преди да напишете генератор, си струва да разберете кои полета всъщност поддържа всеки формат, къде живеят физически тези полета и единственото определящо правило, което управлява дали даден .xlsx файл записва изобщо някакви метаданни.

Два формата, два модела за съхранение

Причината, поради която една библиотека за електронни таблици се нуждае от две внедрявания на метаданни, и причината, поради която наполовина завършените инструменти маркират правилно единия формат и забравят другия, е, че .xls и .xlsx пазят свойствата си на несвързани места. BIFF работната книга ги записва в OLE съставни файлови потоци (compound file streams), главно набора от свойства SummaryInformation, който предхожда самия Excel, заедно с поточния запис WRITEACCESS, който указва кой последно е записал файла. Работната книга на OOXML ги съхранява като XML части в zip пакета, разделени по предназначение: docProps/core.xml съдържа Dublin Core полетата (заглавие, създател, тема, ключови думи, дати), а docProps/app.xml съдържа полетата на ниво приложение, като компания и генериращо приложение, съгласно ECMA-376 Част 1.

HotXLS обединява двата модела за съхранение в директни свойства на обекта на работната книга. Никога не отваряте поток от набор от свойства и не редактирате XML часть на ръка. Присвоявате низове и дати на работната книга и правилният контейнер се материализира за формата, който записвате.

Маркиране на генерирани работни книги от бизнес записа

От страна на XLSX, TXLSXWorkbook излага Title, Subject, Author, Keywords, Description, Category, LastModifiedBy, Company, Application и AppVersion като низове, плюс Created и Modified като стойности на TDateTime, където нула означава незададена стойност. Правилото, което затваря празнината при наследяването, е следното: присвоявайте всяко поле при всяко изпълнение, като вземате стойностите от бизнес записа, вместо да се доверявате на това, което шаблонът случайно съдържа.

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open('statement-template.xlsx') <> 1 then
      raise Exception.Create('Template not available');

    // Overwrite every field: anything left untouched is
    // inherited from whoever designed the template.
    Book.Title := 'Account Statement 2026-06 / ACME Corp';
    Book.Subject := 'Monthly account statement';
    Book.Author := 'Billing Service 4.2';
    Book.LastModifiedBy := 'Billing Service 4.2';
    Book.Company := 'Northwind Financial';
    Book.Category := 'Customer Delivery';
    Book.Keywords := 'statement;billing;2026-06;acct-10024';
    Book.Description := 'Generated document - manual edits are not retained';
    Book.Created := Now;
    Book.Modified := Now;

    Book.SaveAs('statement-10024.xlsx');
  finally
    Book.Free;
  end;
end;

Полето Keywords заслужава повече внимание, отколкото обикновено му се обръща. Търсачките го индексират буквално â€?както Windows Search, така и SharePoint и повечето DMS продукти â€?така че конвенция с разделител точка и запетая, съдържаща номера на акаунта и периода, превръща всяка доставена работна книга в откриваем запис без двупосочно пътуване до базата данни. Този голям обхват обаче има и уловка. Свойствата пътуват с всяко копие на файла, далеч отвъд контрола на достъпа на системата, която ги е написала, така че личните данни нямат място там.

Двойката клейма за време носи семантика, която си струва да се фиксира в правилата, вместо да се оставя на навика. Created трябва да отбелязва момента, в който вашият процес е генерирал документа, и след това да остане непроменен. Modified е полето, което Excel актуализира винаги, когато получателят запише файла, така че разминаването между двете след доставката е доказателство, че някой е редактирал работната книга надолу по веригата, което решава не един спор за това чии числа съдържа препратената електронна таблица. Един капан се крие в незададеното състояние: това е буквалната стойност нула, а не изключение или null, така че одитният код трябва изрично да тества за нула. Форматирайте незададен TDateTime без тази защита и вашите регистрационни файлове ще се напълнят с уверено грешна дата от декември 1899 г.

DocPropsTouched: работната книга, която се доставя без docProps

Флагът само за четене, DocPropsTouched, контролира записващото устройство на свойства в XLSX. Работна книга, в която никога не е било присвоено свойство, не генерира никакви docProps части; HotXLS отказва да запише празен скелет от метаданни. Това поведение е чисто и има две последствия, които си струва да бъдат предвидени.

Кодът за приемане от страна на консуматора не трябва да приема, че core.xml съществува във всеки пакет. Инструмент, който го изисква твърдо, ще отхвърли напълно валидни минимални файлове. И ако вашите правила за съответствие изискват всеки изходящ документ да носи поне идентичността на генератора, това изискване се превръща в код, а не в свойство на формата: присвоявайте Application и Author безусловно по пътя на записване, тъй като недокосната работна книга е напълно законна според спецификацията, макар и тихо да нарушава вашите правила.

Наследственият XLS интерфейс и капанът с Comments

BIFF интерфейсът носи по-стария и по-малък набор от полета: Title, Subject, Author, Keywords, Comments, Company и Manager, плюс LastSavedBy, псевдоним на UserName, който записва WRITEACCESS записа, който Excel показва, когато друг потребител е заключил файла.

var
  Legacy: IXLSWorkbook;     // reference-counted interface: no manual Free
begin
  Legacy := TXLSWorkbook.Create;
  if Legacy.Open('archive-1999.xls') <= 0 then
    raise Exception.Create('Cannot open archive file');

  Legacy.Title := 'FY1999 ledger (migrated copy)';
  Legacy.Author := 'Archive Migration Batch';
  Legacy.Company := 'Northwind Financial';
  Legacy.Comments := 'Migrated 2026-06-11; source retained in cold storage';
  Legacy.LastSavedBy := 'migration-svc';   // BIFF WRITEACCESS record

  Legacy.SaveAs('archive-1999-stamped.xls');
end;

Едно сблъскване на имена причинява повтарящо се объркване. Свойството Comments на ниво документ тук е коментар със свободен текст, показан в диалоговия прозорец със свойствата на файла. То няма нищо общо с коментарите на клетките, които са обекти на чертожния слой, прикрепени към диапазони чрез напълно отделен API. Преглед на код, който приема „вечÐ?пишем Commentsâ€? без да провери кой точно се има предвид, е приел твърдение за грешна функция â€?и това се случва по-често, отколкото общото име предполага. Двете споделят еднакво име, но нито един байт споделено хранилище.

Четене на метаданни при приемане и разликата със сондирането

Четенето е симетрично. След Open същите свойства се връщат попълнени от файла, което превръща одита на метаданните на входящите работни книги в кратък цикъл.

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open(FileName) = 1 then
    begin
      Writeln(Format('%s | title="%s" author="%s" created=%s',
        [ExtractFileName(FileName), Book.Title, Book.Author,
         FormatDateTime('yyyy-mm-dd', Book.Created)]));
      if Book.Created = 0 then
        Writeln('  no creation date recorded');
    end;
  finally
    Book.Free;
  end;
end;

Планирайте работата си около едно ограничение. Няма сонда само за свойства. GetSheetNames може да изброява листове, без да зарежда работна книга, но четенето на Title или Author изисква пълно Open извикване, така че сортирането на метаданни в голям архив плаща пълната цена на анализа за всеки файл. От страна на BIFF можете да намалите тези разходи при одити само за четене, като зададете _DisableGraphics на true преди отваряне, което напълно пропуска чертожния слой. Това е подходящо за цикъл, който чете само свойства и статистика на клетките, но е напълно погрешно в момента, в който същият екземпляр може да бъде записан, тъй като пропуснатото чертожно съдържание би било изгубено. Когато структурата на листа сама по себе си може предварително да филтрира набора (износът на един лист е очевидното нещо за пропускане), евтините техники в нашата статия за списъка с листове и лека инспекция намаляват броя на файловете, които достигат до скъпото преминаване. А при задачи за масово маркиране, където се пишат хиляди изходи вместо да се инспектират, моделите на пропускателна способност при запис от нашата статия за потоково писане за пакетни задания се пренасят без промяна, тъй като присвояването на свойства не добавя нищо измеримо към времето за запис.

Пресичане на формати и ограничаване на течовете

Свойствата преминават чисто в рамките на един и същ интерфейс: отваряте .xlsx, редактирате го, записвате го и наборът се връща непокътнат. Пресичането на формати обаче е мястото, където предположението за паритет се срива, тъй като наборите от полета на BIFF и OOXML не съвпадат едно към едно. BIFF има Manager и няма времеви клейма; OOXML има Category, Description, и двойката Created/Modified. Конвертор, който копира сляпо, губи всичко, което целевият формат не може да съдържа, така че картографирайте полетата изрично и поставете това картографиране във вашия контролен списък за конвертиране до всичко останало, което не оцелява при прехода.

Течът, който отваря наследяването на шаблони, работи в обратна посока: информация, за която никога не сте възнамерявали да изпращате. Имена на автори, вътрешни етикети на проекти, паркирани в ключови думи, чернова на заглавие, която никой не е изчистил. Дисциплината за презаписване на всичко от генератора по-горе е цялостната защита и си струва да се провери по начина, по който би го направил външен човек â€?чрез отваряне на прозореца Properties, до който всеки клиент има достъп, или чрез разархивиране на .xlsx и четене на docProps/core.xml директно от пакета. Това, което виждате там, е точно това, което вижда всеки индексатор по веригата.

Тази видимост надолу по веригата е и причината няколко полета да заслужават повече грижи от останалите. Title, Author, Keywords (които излизат като Tags) и Comments или Description носят по-голямата част от тежестта при индексиране в SharePoint и Windows Search. Заглавие, което е наистина уникално за всеки документ, съдържащо периода и акаунта, прави много повече за откриваемостта, отколкото всяка схема за именуване на папки, надградена над него, а струва само едно присвояване при запис.

Свойствата на документа са най-евтиният професионален блясък, който генерираната работна книга може да носи, и най-често срещаният дефект при доставка, когато никой не отговаря за тях. И двете повърхности на свойствата, описани тук, принадлежат на HotXLS Component, който ги записва вградено за XLS и XLSX без автоматизация на Excel.