Перейти к содержанию

Формирование простого Excel на js

Иногда возникает необходимость сохранить отображаемую таблицу в Excel.

В таком случае HTML-код таблицы можно сохранить в файл и выдать сохранение как .xls. При открытии HTML, сохранённого как .xls, Excel будет выдавать уведомление о том, что файл небезопасен или повреждён. Однако, если подтвердить открытие, таблица откроется в Excel-файле.

Расширение файла должно быть .xls. Сохранить в .xlsx таким способом не получится.

Описанные ниже методы не создают полноценный Excel-файл, а лишь позволяют передать Excel фрагмент HTML-разметки, который он сможет открыть как таблицу с предупреждениями. Для создания полноценного Excel-файла необходимо использовать специализированные библиотеки.

Сохранение HTML-кода таблицы в .xls

Чтобы в Excel корректно отображались символы, необходимо указать кодировку и добавить в начало файла корректный BOM-символ:

new Uint8Array([0xEF, 0xBB, 0xBF]);

<button id="saveExcel">Скачать Excel</button>
<table id="table"></table>

<script>
  document.getElementById('saveExcel').addEventListener('click', () => {
    const table = document.getElementById('table');
    const blob = new Blob(
      [ new Uint8Array([0xEF, 0xBB, 0xBF]), table.outerHTML ],
      { type: 'application/excel;charset=utf-8' }
    );
    const anchor = document.createElement('a');
    document.body.appendChild(anchor);
    const url = window.URL.createObjectURL(blob);
    anchor.href = url;
    anchor.download = 'Excel file name.xls';
    anchor.click();
    anchor.remove();
    window.URL.revokeObjectURL(url);
  });
</script>

Вписывание стилей в код

При генерации кода таблицы с помощью Vue пропускаются теги <style>, <script>. Внедрение специальных стилей можно обойти используя <component is="style"/>:

<template>
  <div ref="excelTable">
    <component is="style">
      .string { mso-number-format: "@"; }
      .number { mso-number-format: "#\,##0.00"; }
      br { mso-data-placement:same-cell; }
    </component>
    <table></table>
  </div>
</template>

Стили для отображения данных в Excel

<style>
  .string  { mso-number-format: "@"; }
  .number  { mso-number-format: "#\,##0.00"; }
  .integer { mso-number-format: "0"; }
  br       { mso-data-placement: same-cell; } /* Перенос строки */
</style>

Excel через промежуточный XML

У Excel файла, созданного предыдущим образом, не будет привычной сеточной разметки.

Для привычного отображения файла в Excel необходимо создать документ специального вида:

<html
  xmlns:o="urn:schemas-microsoft-com:office:office"
  xmlns:x="urn:schemas-microsoft-com:office:excel"
  xmlns="http://www.w3.org/TR/REC-html40"
>
  <head>
    <!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>sheetName</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
  </head>
  <body>
    <table></table>
  </body>
</html>

Обратите внимание на узел <x:Name>sheetName</x:Name>. С помощью этого узла можно указать название листа для Excel. В LibreOffice этот узел не читается. Поэтому для совместимости вся строка заключена в [if gte mso 9].

Так же преимуществом данного способа является возможность указать кодировку без использования BOM-символов, достаточно в <head> добавить <meta>:

<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
</head>

Пример

/**
 * @param table      HTML код таблицы, которую надо выгрузить в Excel
 * @param sheetName  Заголовок листа для Excel
 */
const generateExcelDocument = (table, sheetName = '') => `
  <html
    xmlns:o="urn:schemas-microsoft-com:office:office"
    xmlns:x="urn:schemas-microsoft-com:office:excel"
    xmlns="http://www.w3.org/TR/REC-html40"
  >
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>${sheetName}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
  </head>
  <body>${table}</body>
  </html>
`;

document.getElementById('saveExcel').addEventListener('click', () => {
    const blob = new Blob(
      [ generateExcelDocument(document.getElementById('table').outerHTML, 'Список товаров') ],
      { type: 'application/vnd.ms-excel;charset=utf-8' }
    );
    const anchor = document.createElement('a');
    document.body.appendChild(anchor);
    const url = window.URL.createObjectURL(blob);
    anchor.href = url;
    anchor.download = 'Excel file name.xls';
    anchor.click();
    anchor.remove();
    window.URL.revokeObjectURL(url);
  });

Внедрение стилей в Excel файл

Чтоб стили <style> корректно обрабатывались Excel'ем - они должны быть помещены в раздел <head> полученного документа.

Excel не поддерживает множество классов на элементе. Если написать так - Excel не распознает ни одного класса. Можно указать только один класс

<!-- в таком виде ни один из классов не будет распознан -->
<td class="first second"></td>

<!-- корректный способ указать класс -->
<td class="general"></td>

Так же надо учитывать, что некоторые единицы в Excel отличаются от html. Например, у размера шрифта вместо пикселей px указываются пойнты pt (типографический пункт).

td {
  font-size: 11.0pt; /* размер шрифта */
  font-weight: 700;  /* жирный шрифт */
}