XLSX изнутри: почему потоковая запись решает почти все проблемы

aVadim 06.02.2026 23:10

Чтобы понять, почему потоковая запись в XLSX-файл настолько эффективна, нужно понять, что XLSX — это обычный ZIP-архив. Если переименовать его в .zip и распаковать, внутри мы увидим множество xml-файлоа, примерно так:

  • [Content_Types].xml
  • xl/workbook.xml
  • xl/styles.xml
  • xl/sharedStrings.xml
  • xl/worksheets/sheet1.xml

И если учесть, что ни один из этих файлов не требует знания всех данных заранее, то именно это делает потоковую работу возможной.

Данные в XLSX и что с ними делает объектная модель

Основные данные листа хранятся в xl/worksheets/sheet1.xml.

Упрощённо он выглядит так:

<worksheet>
  <sheetData>
    <row r="1">
      <c r="A1"><v>Header 1</v></c>
      <c r="B1"><v>Header 2</v></c>
    </row>
    <row r="2">
      <c r="A2"><v>Value 1</v></c>
      <c r="B2"><v>Value 2</v></c>
    </row>
  </sheetData>
</worksheet>

Ключевой момент: строки идут строго последовательно, а Excel не требует ни знать количество строк заранее, ни хранить предыдущие строки в памяти, ни иметь доступ ко всем данным сразу.

Но большинство популярных библиотек работают так:

  • Создают объект Workbook
  • Создают объект Worksheet
  • Для каждой строки создают объект Row
  • Для каждой ячейки создают объект Cell

Все объекты живут в памяти до сохранения файла. При 300 000 строк × 10 колонок - это 3 миллиона объектов ячеек, плюс строки, плюс стили, плюс много чего еще. Даже если каждая ячейка занимает всего несколько сотен байт, то счёт, в итоге, идёт на гигабайты.

Потоковая модель: что меняется принципиально

Потоковая запись делает одну простую вещь: пишет XML сразу в файл и забывает о нём. И никаких объектов книги, никакой «модели документа», никакого хранения данных в памяти.

Память используется только на текущую строку, на текущие стили, на служебные буферы.

Тут отмечу, что в XLSX стили вынесены в отдельный файл — styles.xml.

Пример:

<cellXfs count="3">
  <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
  <xf numFmtId="14" fontId="0" fillId="0" borderId="0"/>
  <xf numFmtId="0" fontId="1" fillId="0" borderId="0"/>
</cellXfs>

В ячейке хранится только индекс:

<c r="A2" s="1"><v>45234</v></c>

Это означает, что стиль создаётся один раз, а дальше используется по индексу, и Excel сам всё связывает. Поэтому потоковая запись не мешает стилям, она просто требует кешировать стили, переиспользовать индексы и не создавать дубликаты.

Почему это не универсальное решение

Есть кейсы, где потоковая модель не подходит. Например, если нужно редактировать произвольные ячейки, «прыгать» по листу.

Однако, если речь идет о генерации сравнительно небольших XLSX-файлов, когда хранение всех данных в памяти не является проблемой, то и тут FastExcelWriter вполне справляется с этой задачей - библиотеку можно перевести в режим «прямой» записи в произвольные ячейки, и тогда данные будут накапливаться в мамяти, а по окончании построчно будут записаны в файл.

Но об этом будет рассказано в следующих публикациях.

Комментарии (0)