Техническая статья

Загрузка XLSX только по метаданным и с выбором листов в HotXLS

HotXLS умеет открывать книгу XLSX и не разбирать ни одной ячейки листа. TXLSXWorkbook.MetadataOnly читает свойства книги, определённые имена, имена и видимость листов, стили, тему, подключения и внешние ссылки, а затем останавливается до cell-XML. Для сканера, которому нужно знать, что в каталоге книг лежит, это разница между минутами и секундами

Три связанных переключателя дополняют картину: SelectedSheets разбирает ячейки для именованного подмножества, LoadSheet материализует пропущенный лист позже из исходного архива, а PreserveRawParts копирует сжатые байты неизменённых частей напрямую при сохранении. Вместе они превращают «открыть книгу» из одной операции «всё или ничего» в то, что вы можете ограничить

Что можно узнать, не разобрав ни одной ячейки?

Больше, чем нужно большинству задач обнаружения. С MetadataOnly, установленным до Open, книга возвращает свои имена листов и состояния видимости, определённые имена, свойства документа, стили и тему, внешние ссылки и подключения. Листы присутствуют как объекты с нулём ячеек

Это именно та информация, на которой работает каталог, аудит или решение о маршрутизации. Какие книги определяют имя TaxRate? В каких есть скрытые листы? Какие ссылаются на сервер, выведенный из эксплуатации? Отвечать на это полной загрузкой каждого файла — то, как ночной job превращается в ночную работу на всю ночь

var
  Workbook: TXLSXWorkbook;
  I: Integer;
begin
  Workbook := TXLSXWorkbook.Create(nil);
  try
    Workbook.MetadataOnly := True;      // set before Open
    Workbook.Open(FileName);
    for I := 1 to Workbook.Sheets.Count do      // Sheets[] is 1-based
      if not Workbook.Sheets[I].Visible then
        Report.Add(FileName + ': hidden sheet ' +
          Workbook.Sheets[I].Name);
  finally
    Workbook.Free;
  end;
end;

Загрузка двух листов из сорока

SelectedSheets — это TStringList имён листов. Когда список непуст, Open разбирает cell-XML только для названных в нём листов; каждый другой лист сохраняет корректное имя и состояние видимости и не содержит ячеек. Пустой список означает прежнее поведение — загрузить всё

Взаимодействие с MetadataOnly стоит проговорить прямо, потому что ошибка здесь даёт озадачивающую пустую книгу: MetadataOnly = True перекрывает выборку и пропускает все листы. Используйте одно или другое. Обнаружение использует только метаданные; целевая работа — выборку

Workbook.SelectedSheets.Add('Summary');
Workbook.SelectedSheets.Add('Q3 Detail');
Workbook.Open('consolidated-2026.xlsx');
// Later, when the user opens a tab you skipped.
// LoadSheet takes the 0-based position and needs the source file,
// so it does not work on a workbook opened from a stream
if Workbook.LoadSheet(ZeroBasedPosition) then
  Grid.Refresh;

LoadSheet(Index) материализует пропущенный лист по требованию из исходного архива — именно это делает шаблон пригодным в интерактивном приложении, а не только в пакете. Откройте с листом, на который смотрит пользователь, загружайте остальные по щелчку. Архив остаётся открытым именно для этого, поэтому держите книгу живой, пока пользователь может запросить другой лист

Почему обход туда-обратно меняет части, которых вы не касались?

Потому что наивное сохранение перегенерирует всё. Разверните тему, постройте объектную модель, сериализуйте обратно, сжмите заново — и вот байты уже отличаются, хотя в теме ничего не изменилось. Для VBA-проектов и кэшей сводных таблиц это хуже, чем расточительно, потому что эти части несут структуру, которую объектная модель не полностью представляет

PreserveRawParts отвечает на это копированием сжатого представления неизменённых частей напрямую из исходного архива при SaveAs. Тема, VBA-проект, сводные таблицы и кэши сводных переносятся как байты, когда они не модифицировались. Без декомпрессии, без рекомпрессии, без риска что-то потерять в цикле сериализации

Цена — ограничение, которое у вас и так было: исходный архив должен быть всё ещё доступен во время сохранения. Если ваш рабочий процесс читает файл в память, закрывает его и сохраняет через час, пути сырого копирования брать неоткуда. Держите источник достижимым в течение жизни книги — это то же требование, что и для LoadSheet

Конвейер обнаружения, намеренно открывающий дважды

Шаблон, работающий в продакшене, — два прохода без хитростей. Первый проход открывает только метаданные и принимает решение. Второй проход открывает заново файлы, что важны, с выборкой листов и выполняет работу. Повторное открытие кажется расточительным, пока не сочтёшь, чего первый проход избежал: второе открытие — единственное, разбирающее ячейки, и разбирает оно лишь листы, выбранные первым

// Pass 1: discovery
Workbook.MetadataOnly := True;
Workbook.Open(FileName);
Wanted := SheetsMatching(Workbook, 'Invoice');
Workbook.Free;

// Pass 2: targeted load
Workbook := TXLSXWorkbook.Create(nil);
Workbook.SelectedSheets.AddStrings(Wanted);
Workbook.Open(FileName);

Не пытайтесь повысить книгу только-метаданные на месте, сбросив флаг после Open. Флаг читается во время открытия; сброс после него ничего не меняет для книги, чей cell-XML так и не был посещён

Как это соотносится с другими рычагами больших файлов

Избирательная загрузка снижает то, что вы разбираете. Она не снижает то, что стоит лист после загрузки, и не помогает, когда вам действительно нужна каждая ячейка каждого листа. Для этого случая соответствующая работа — на стороне хранения, описанная в заметках о блочном хранении ячеек по строкам и накладках интервальных стилей, и на стороне разбора — в параллельном разборе XLSX и распределителе памяти. Когда задача — чистое сканирование без правки, потоковый прямой читатель совсем минует объектную модель и обойдёт любую стратегию загрузки, её строящую

HotXLS открывает файлы XLS и XLSX из нативного кода Delphi и C++Builder без зависимости от Excel, поэтому серверная задача обнаружения работает с установленным только вашим исполняемым файлом — см. страницу компонента HotXLS для поддерживаемых форматов и лицензирования