Một bảng tính mang hai lớp danh tính. Có lưới các ô, và có siêu dữ liệu tài liệu đi kèm: tiêu đề, tác giả, công ty, từ khóa, các timestamp. Excel không bao giờ hiển thị lớp thứ hai đó trong lưới, nhưng đó lại là lớp mà Windows Search thu thập chỉ mục, lớp mà SharePoint đọc để đặt tiêu đề cho tài liệu, và lớp mà một hệ thống quản lý hồ sơ lưu trữ theo đó. Khi một workbook được tạo ra kế thừa Tác giả (Author) và Tiêu đề (Title) từ template mà nó được xây dựng, mọi hệ thống phía sau sẽ ghi nhận nhà thiết kế template là tác giả của bốn nghìn báo cáo khách hàng. Siêu dữ liệu không đúng ở bất kỳ đâu và lại được tham chiếu ở mọi nơi
HotXLS cung cấp lớp này dưới dạng các thuộc tính thông thường cấp workbook trên cả hai engine của nó: facade BIFF cho .xls và facade OOXML cho .xlsx. Bạn đọc một trường sau khi mở file và ghi một trường trước khi lưu file. Thư viện quyết định container vật lý nào giá trị sẽ nằm trong đó. Những gì đáng hiểu trước khi bạn viết một trình tạo là các trường nào mỗi định dạng thực sự hỗ trợ, nơi các trường đó thực sự tồn tại, và quy tắc duy nhất chi phối việc một file .xlsx có ghi nhận bất kỳ siêu dữ liệu nào hay không
Hai định dạng, hai mô hình lưu trữ
Lý do một thư viện bảng tính cần hai triển khai siêu dữ liệu, và lý do tại sao các công cụ chưa hoàn thiện thường đóng dấu đúng một định dạng và bỏ quên định dạng kia, là vì .xls và .xlsx giữ các thuộc tính của chúng ở những nơi không liên quan. Một BIFF workbook ghi chúng vào các stream OLE compound-file, chủ yếu là tập hợp thuộc tính SummaryInformation có trước bản thân Excel, bên cạnh bản ghi WRITEACCESS trong stream đặt tên cho bất kỳ ai đã lưu file cuối cùng. Một OOXML workbook giữ chúng dưới dạng các phần XML bên trong package zip, được chia theo mục đích: docProps/core.xml giữ các trường Dublin Core (title, creator, subject, keywords, dates) và docProps/app.xml giữ các trường cấp ứng dụng như công ty và ứng dụng tạo ra, theo ECMA-376 Part 1
HotXLS làm phẳng cả hai mô hình lưu trữ đó thành các thuộc tính trực tiếp của đối tượng workbook. Bạn không bao giờ phải tự tay mở một property-set stream hoặc chỉnh sửa một XML part. Bạn gán các chuỗi và ngày tháng cho workbook, và container chính xác sẽ tự động hình thành cho định dạng nào bạn lưu
Đóng dấu các workbook được tạo từ bản ghi nghiệp vụ
Về phía XLSX, TXLSXWorkbook hiển thị Title, Subject, Author, Keywords, Description, Category, LastModifiedBy, Company, Application, và AppVersion dưới dạng chuỗi, cộng với Created và Modified dưới dạng các giá trị TDateTime nơi số không có nghĩa là chưa thiết lập. Quy tắc để giải quyết vấn đề kế thừa chỉ là một câu: gán mọi trường trên mỗi lần chạy, lấy các giá trị từ bản ghi nghiệp vụ thay vì tin tưởng vào bất kỳ thứ gì template mang theo
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;
Trường Keywords xứng đáng được suy nghĩ nhiều hơn mức thông thường. Hạ tầng tìm kiếm thu thập chỉ mục nó nguyên văn, từ Windows Search, SharePoint, cho đến hầu hết các sản phẩm DMS, vì vậy một quy ước phân tách bằng dấu chấm phẩy mang số tài khoản và kỳ kế toán sẽ biến mỗi workbook được giao thành một bản ghi có thể tìm thấy mà không cần vòng lặp truy vấn database. Điểm tương tự cũng là cái bẫy. Các thuộc tính đi theo mỗi bản sao của file, vượt qua cả các kiểm soát quyền truy cập của hệ thống đã ghi chúng, vì vậy dữ liệu cá nhân không thuộc về nơi đó
Cặp timestamp mang ngữ nghĩa xứng đáng được cố định trong chính sách thay vì để theo thói quen. Created nên đánh dấu thời điểm pipeline của bạn tạo ra tài liệu và sau đó giữ nguyên. Modified là trường Excel cập nhật bất cứ khi nào người nhận lưu file, vì vậy sự khác biệt giữa hai trường sau khi giao là bằng chứng rõ ràng cho thấy ai đó đã chỉnh sửa workbook ở phía sau, điều này giúp giải quyết nhiều tranh chấp về việc số liệu của ai thực sự nằm trong một bảng tính được chuyển tiếp. Một cái bẫy ẩn trong trạng thái chưa thiết lập: nó là giá trị literal bằng không, không phải là một exception hay một null, vì vậy code kiểm toán phải kiểm tra số không một cách rõ ràng. Định dạng một TDateTime chưa thiết lập mà không có bảo vệ đó sẽ làm cho nhật ký của bạn đầy các ngày tháng 12 năm 1899 sai lệch hoàn toàn
DocPropsTouched: workbook xuất xưởng không có docProps
Một cờ chỉ đọc, DocPropsTouched, kiểm soát trình viết thuộc tính XLSX. Một workbook không có thuộc tính nào được gán sẽ không tạo ra bất kỳ phần docProps nào; HotXLS từ chối ghi một khung siêu dữ liệu trống. Hành vi này rất gọn gàng, và nó có hai hệ quả đáng để thiết kế xung quanh
Code tiếp nhận ở phía tiêu thụ không được giả định rằng core.xml tồn tại trong mọi package. Một công cụ bắt buộc phải có nó sẽ từ chối các file tối giản hoàn toàn hợp lệ. Và nếu chính sách tuân thủ của bạn yêu cầu mọi tài liệu gửi đi phải mang danh tính của trình tạo, yêu cầu đó phải được chuyển hóa thành code thay vì một thuộc tính của định dạng: gán Application and Author vô điều kiện trong đường dẫn lưu, vì một workbook không được chạm tới hoàn toàn hợp lệ theo đặc tả trong khi lại âm thầm vi phạm chính sách của bạn
Bề mặt XLS cũ và cái bẫy Comments
Facade BIFF mang tập hợp trường cũ hơn, nhỏ hơn: Title, Subject, Author, Keywords, Comments, Company, và Manager, cộng với LastSavedBy, một bí danh của UserName, ghi lại bản ghi WRITEACCESS mà Excel hiển thị khi một người dùng khác đang khóa file
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;
Một sự trùng lặp tên gọi gây ra sự nhầm lẫn thường xuyên. Thuộc tính Comments ở cấp tài liệu ở đây là những nhận xét văn bản tự do được hiển thị trong hộp thoại thuộc tính của file. Nó hoàn toàn không liên quan đến cell comment (bình luận trong ô), vốn là các đối tượng lớp bản vẽ được đính kèm vào các phạm vi thông qua một API hoàn toàn tách biệt. Một đợt duyệt code chấp nhận việc "chúng tôi đã viết Comments" mà không kiểm tra xem thuộc tính nào được nói tới đã chấp nhận một tuyên bố về sai tính năng, và điều đó xảy ra thường xuyên hơn mức mà cái tên chung gợi ý. Hai thứ này chia sẻ chung các chữ cái và không chia sẻ một byte bộ nhớ nào
Đọc siêu dữ liệu khi tiếp nhận, và khoảng trống thăm dò
Đọc là đối xứng. Sau khi Open, các thuộc tính tương tự được trả về đầy đủ từ file, giúp việc kiểm toán siêu dữ liệu của các workbook đi vào trở thành một vòng lặp ngắn
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;
Hãy lập kế hoạch xung quanh một giới hạn khi bạn thực hiện. Không có tính năng thăm dò chỉ-đọc-thuộc-tính. GetSheetNames có thể liệt kê các sheet mà không cần tải workbook, nhưng đọc Title hoặc Author có nghĩa là phải thực hiện một lệnh Open đầy đủ, do đó việc phân loại siêu dữ liệu trên một kho lưu trữ lớn phải trả toàn bộ chi phí phân tích cú pháp trên mỗi file. Về phía BIFF, bạn có thể giảm chi phí đó cho các đợt kiểm toán chỉ đọc bằng cách đặt _DisableGraphics thành true trước khi mở, điều này sẽ bỏ qua hoàn toàn lớp bản vẽ. Nó phù hợp với một vòng lặp chỉ đọc các thuộc tính và thống kê ô, và hoàn toàn sai ngay khi thực thể tương tự có thể được lưu, vì nội dung bản vẽ bị bỏ qua sẽ bị mất. Khi cấu trúc sheet đơn thuần có thể lọc trước tập hợp, với việc xuất các sheet đơn lẻ là thứ rõ ràng cần bỏ qua, các kỹ thuật tiết kiệm trong bài viết về liệt kê sheet và kiểm tra nhẹ nhàng sẽ giảm số lượng file phải chuyển tới bước xử lý đắt đỏ. Và trên các công việc đóng dấu hàng loạt, nơi hàng nghìn đầu ra được ghi thay vì kiểm tra, các mô hình throughput phía ghi trong bài viết về ghi dạng stream cho các công việc batch được áp dụng mà không có thay đổi nào, vì việc gán thuộc tính không thêm bất kỳ chi phí đáng kể nào vào thời gian lưu
Chéo định dạng và hạn chế rò rỉ
Các thuộc tính round-trip trơn tru bên trong một facade duy nhất: mở một file .xlsx, chỉnh sửa nó, lưu nó, và tập hợp thuộc tính trở lại nguyên vẹn. Việc chuyển đổi chéo các định dạng là nơi mà giả định về tính tương đương bị phá vỡ, vì tập hợp trường của BIFF và OOXML không khớp nhau một-một. BIFF có Manager và không có timestamp; OOXML có Category, Description, và cặp Created/Modified. Một bộ chuyển đổi sao chép một cách mù quáng sẽ làm mất bất kỳ thuộc tính nào mà định dạng đích không thể chứa, vì vậy hãy ánh xạ các trường một cách rõ ràng và đưa việc ánh xạ đó vào danh sách kiểm tra chuyển đổi của bạn bên cạnh mọi thứ khác không tồn tại sau chuyến đi
Rò rỉ mà sự kế thừa template mở ra lại chạy theo hướng ngược lại: thông tin bạn không bao giờ muốn gửi ra ngoài. Tên tác giả, các nhãn dự án nội bộ được đặt trong từ khóa, một tiêu đề nháp không ai xóa. Kỷ luật ghi đè mọi thứ từ trình tạo ở trên là toàn bộ lá chắn bảo vệ, và việc xác minh nên được thực hiện theo cách mà một người bên ngoài sẽ làm, bằng cách mở hộp thoại Thuộc tính (Properties) mà bất kỳ khách hàng nào cũng có thể tiếp cận hoặc bằng cách giải nén file .xlsx và đọc docProps/core.xml trực tiếp từ package. Những gì bạn thấy ở đó chính là những gì mọi trình thu thập chỉ mục phía sau nhìn thấy
Khả năng hiển thị phía sau đó cũng là lý do một vài trường cần được chăm sóc nhiều hơn phần còn lại. Title, Author, Keywords (hiển thị dưới dạng Tags), và Comments hoặc Description mang hầu hết trọng lượng lập chỉ mục trong SharePoint và Windows Search. Một Title thực sự khác biệt trên mỗi tài liệu, mang kỳ báo cáo và tài khoản, sẽ giúp ích cho việc tìm kiếm nhiều hơn bất kỳ sơ đồ đặt tên thư mục nào được xếp chồng lên nó, và nó chỉ tốn một dòng gán trên mỗi lần lưu
Các thuộc tính tài liệu là sự hoàn thiện chuyên nghiệp tiết kiệm nhất mà một workbook được tạo ra có thể mang theo, và là lỗi thường thấy nhất khi không ai quản lý chúng. Cả hai bề mặt thuộc tính được mô tả ở đây đều thuộc về HotXLS Component, thư viện ghi chúng một cách tự nhiên cho XLS và XLSX mà không cần tự động hóa Excel