Выгрузка таблицы значений в файл XLSX: код, COM и CSV
Содержание
Чтобы сохранить таблицу значений в Excel из 1С, выведите её в ТабличныйДокумент и вызовите ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX). Установленный Excel для этого не нужен: файл формирует сама платформа 1С:Предприятие 8.3.
Если таблица уже видна на экране в форме, программировать ничего не надо: Ещё → Вывести список…, затем сохранить полученный табличный документ в формате XLSX. COM-объект Excel и CSV нужны в особых случаях: сложное оформление или очень большие объёмы.
Что решить до выгрузки
Три вопроса определяют, какой код писать. Ответьте на них заранее, иначе файл окажется не там или не в том виде.
- 🖥️ Где выполняется код. Таблица значений живёт на сервере. Метод
Записать(), вызванный на сервере, создаёт файл на диске сервера, а не на компьютере пользователя. - 📦 Есть ли Excel у пользователя. Табличному документу Excel не нужен. COM-объекту нужен установленный Microsoft Excel и Windows.
- 📏 Сколько строк. До десятков тысяч строк подходит любой способ. На сотнях тысяч табличный документ съедает память, а COM работает медленно, если писать по ячейке.
В управляемой форме таблица хранится в реквизите типа ДанныеФормыКоллекция. Чтобы получить настоящую таблицу значений, на сервере вызовите РеквизитФормыВЗначение("ИмяРеквизита").
Лимиты самого Excel тоже стоит держать в голове: в формате XLSX на листе помещается 1 048 576 строк, в старом XLS — только 65 536.
Стандартные средства платформы: вывод в табличный документ и запись XLSX
Без программирования: «Вывести список»
Подходит, когда таблица отображается в любой форме — списке, обработке, отчёте.
- Откройте форму с таблицей.
- Нажмите
Ещё → Вывести список…. - Отметьте нужные колонки и выберите вывод в табличный документ.
- В открывшемся документе откройте главное меню окна (кнопка с тремя полосками) и выберите
Файл → Сохранить как…. - В поле типа файла укажите лист Excel в формате
*.xlsxи сохраните.
Команда доступна, только если у пользователя есть право «Вывод». Если пункта нет в меню «Ещё», проверьте это право в профиле групп доступа.
Быстрый вариант в коде: построитель отчёта
Классический приём в три строки. Построитель сам создаёт шапку с именами колонок.
&НаСервере
Функция ТаблицаВТабДокБыстро(ТЗ)
ТабДок = Новый ТабличныйДокумент;
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТЗ);
Построитель.Вывести(ТабДок);
Возврат ТабДок;
КонецФункции
Минус — оформление построителя: заголовки, отступы и группировки не всегда нужны в выгрузке для Excel. Объект работает на сервере, в тонком и веб-клиенте его нет.
Полный контроль: заполнение ячеек вручную
Когда нужна чистая таблица «шапка + строки» без лишнего, заполняйте ячейки сами. Главный приём — числа и даты записывать как значения, а не как текст. Тогда в Excel они сразу считаются в формулах.
&НаСервере
Функция ТаблицаВТабДок(ТЗ)
ТабДок = Новый ТабличныйДокумент;
// Шапка
НомерКолонки = 0;
Для Каждого Колонка Из ТЗ.Колонки Цикл
НомерКолонки = НомерКолонки + 1;
Область = ТабДок.Область(1, НомерКолонки);
Область.Текст = ?(ЗначениеЗаполнено(Колонка.Заголовок), Колонка.Заголовок, Колонка.Имя);
Область.Шрифт = Новый Шрифт(Область.Шрифт, , , Истина);
КонецЦикла;
// Строки
НомерСтроки = 1;
Для Каждого СтрокаТЗ Из ТЗ Цикл
НомерСтроки = НомерСтроки + 1;
НомерКолонки = 0;
Для Каждого Колонка Из ТЗ.Колонки Цикл
НомерКолонки = НомерКолонки + 1;
Область = ТабДок.Область(НомерСтроки, НомерКолонки);
Значение = СтрокаТЗ[Колонка.Имя];
Если ТипЗнч(Значение) = Тип("Число") Или ТипЗнч(Значение) = Тип("Дата") Тогда
Область.СодержитЗначение = Истина;
Область.Значение = Значение;
Иначе
Область.Текст = Строка(Значение);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Возврат ТабДок;
КонецФункции
Ширину колонки задают через область колонки: ТабДок.Область("C" + Формат(НомерКолонки, "ЧГ=0")).ШиринаКолонки = 20. Формат с ЧГ=0 нужен, чтобы номер склеился со строкой без пробела-разделителя.
Сама запись — одна строка:
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
Для старого Excel вместо XLSX укажите XLS. Для LibreOffice подойдёт ODS.
Как передать файл Excel на компьютер пользователя
Самая частая ловушка: код на сервере пишет файл в C:\Выгрузка.xlsx, пользователь ищет его у себя и не находит. В клиент-серверной базе файл лёг на диск сервера 1С.
Правильная схема — временный файл на сервере, двоичные данные во временное хранилище, получение на клиенте.
&НаСервере
Функция ВыгрузкаВХранилище()
ТЗ = РеквизитФормыВЗначение("Таблица");
ТабДок = ТаблицаВТабДок(ТЗ);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
ТабДок.Записать(ИмяВременногоФайла, ТипФайлаТабличногоДокумента.XLSX);
Данные = Новый ДвоичныеДанные(ИмяВременногоФайла);
УдалитьФайлы(ИмяВременногоФайла);
Возврат ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);
КонецФункции
&НаКлиенте
Процедура ВыгрузитьВExcel(Команда)
Адрес = ВыгрузкаВХранилище();
ФайловаяСистемаКлиент.СохранитьФайл(Неопределено, Адрес, "Выгрузка.xlsx");
КонецПроцедуры
ФайловаяСистемаКлиент — модуль Библиотеки стандартных подсистем. Он есть в типовых конфигурациях (БП 3.0, ЗУП 3.1, УТ 11) и сам показывает диалог сохранения. В самописной конфигурации без БСП используйте метод глобального контекста ПолучитьФайл(Адрес, "Выгрузка.xlsx", Истина). Он синхронный и работает, только если в свойствах конфигурации разрешены модальные вызовы.
Ещё проще для разовых задач: верните табличный документ на клиент и вызовите ТабДок.Показать(). Пользователь сам сохранит его в XLSX через Файл → Сохранить как….
Выгрузка через COM-объект Excel
COM нужен, когда стандартного табличного документа мало: несколько листов, формулы, автофильтр, условное форматирование, запись в существующий шаблон. Работает только в Windows с установленным Excel. Код выполняется на клиенте (тонкий или толстый клиент, не веб).
Медленный, но понятный вариант — запись по ячейкам:
&НаКлиенте
Процедура ВыгрузитьЧерезCOM(Данные, ПутьКФайлу)
// Данные — массив структур, полученный с сервера
Excel = Новый COMОбъект("Excel.Application");
Попытка
Excel.Visible = Ложь;
Excel.DisplayAlerts = Ложь;
Книга = Excel.Workbooks.Add();
Лист = Книга.Worksheets(1);
Лист.Cells(1, 1).Value = "Номенклатура";
Лист.Cells(1, 2).Value = "Количество";
НомерСтроки = 1;
Для Каждого Элемент Из Данные Цикл
НомерСтроки = НомерСтроки + 1;
Лист.Cells(НомерСтроки, 1).Value = Элемент.Номенклатура;
Лист.Cells(НомерСтроки, 2).Value = Элемент.Количество;
КонецЦикла;
Книга.SaveAs(ПутьКФайлу, 51); // 51 — формат xlsx
Книга.Close(Ложь);
Исключение
Excel.Quit();
ВызватьИсключение;
КонецПопытки;
Excel.Quit();
КонецПроцедуры
На клиент таблицу значений не передать. Поэтому на сервере преобразуйте её в массив структур с примитивными значениями: строки, числа, даты. Ссылки на справочники заранее переводите в строку через Строка().
Блок Попытка…Исключение с Excel.Quit() обязателен. Без него при любой ошибке в памяти остаётся невидимый процесс EXCEL.EXE. Через десяток запусков таких процессов становится много, и Excel перестаёт открывать файлы.
Код формата в SaveAs: 51 — книга XLSX, 56 — книга XLS.
XLSX и CSV без Excel: программное формирование файла
Если Excel на машине нет, а табличный документ не подходит, остаётся CSV. Его открывает любой Excel, а пишется он потоком, построчно, почти без затрат памяти.
&НаСервере
Процедура ЗаписатьCSV(ТЗ, ИмяФайла)
Запись = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);
Поля = Новый Массив;
Для Каждого Колонка Из ТЗ.Колонки Цикл
Поля.Добавить(Колонка.Имя);
КонецЦикла;
Запись.ЗаписатьСтроку(СтрСоединить(Поля, ";"));
Для Каждого СтрокаТЗ Из ТЗ Цикл
Поля.Очистить();
Для Каждого Колонка Из ТЗ.Колонки Цикл
Значение = СтрокаТЗ[Колонка.Имя];
Если ТипЗнч(Значение) = Тип("Число") Тогда
Текст = Формат(Значение, "ЧРД=,; ЧГ=0");
ИначеЕсли ТипЗнч(Значение) = Тип("Дата") Тогда
Текст = Формат(Значение, "ДФ=dd.MM.yyyy");
Иначе
Текст = """" + СтрЗаменить(Строка(Значение), """", """""") + """";
КонецЕсли;
Поля.Добавить(Текст);
КонецЦикла;
Запись.ЗаписатьСтроку(СтрСоединить(Поля, ";"));
КонецЦикла;
Запись.Закрыть();
КонецПроцедуры
Что здесь сделано под Excel:
- 🔤 Кодировка
UTF8— 1С пишет файл с меткой BOM, и Excel правильно показывает кириллицу. ВариантUTF8Nпишет без BOM, и русский текст превращается в «кракозябры». - ➗ Разделитель
;— русская версия Excel ждёт точку с запятой, а не запятую. - 🔢
ЧГ=0— без этого 1С вставит в число неразрывный пробел между разрядами, и Excel прочитает «1 250» как текст. - 📝 Строки в кавычках с удвоением внутренних кавычек — иначе название «ООО «Ромашка»; филиал» разорвёт строку на две колонки.
Настоящий XLSX можно собрать и вручную: это ZIP-архив с XML-файлами внутри. В 1С для этого есть ЗаписьXML и ЗаписьZipФайла. Но схема Office Open XML объёмная, и на практике так делают редко — только когда нужен XLSX на сервере Linux с оформлением, которое табличный документ не передаёт.
Ошибки, кодировка и типы данных при экспорте
Большая часть проблем всплывает уже в Excel, когда файл открыли и что-то выглядит не так.
Отдельно о датах. Пустая дата в 1С — это 01.01.0001. В Excel она выглядит как мусор, поэтому перед выгрузкой проверяйте ЗначениеЗаполнено() и пишите пустую ячейку.
Большие объёмы: как ускорить выгрузку
На таблице в 200–300 тысяч строк разница между способами — минуты против секунд.
Табличный документ
- Получайте
Областьячейки один раз и заполняйте все её свойства через эту переменную, без повторных вызововТабДок.Область(). - Не задавайте шрифт и рамки каждой ячейке строк. Оформите шапку, а данные оставьте без форматирования.
- При нехватке памяти на сервере разбейте выгрузку на несколько файлов по 100–200 тысяч строк.
COM-объект: массив вместо ячеек
Каждое обращение Лист.Cells(i, j).Value — отдельный вызов между процессами 1С и Excel. 10 000 строк по 10 колонок — это 100 000 вызовов. Быстрее собрать данные в двумерный массив COMSafeArray и передать его в диапазон одной операцией:
КолвоСтрок = Данные.Количество();
КолвоКолонок = 2;
Массив = Новый COMSafeArray("VT_VARIANT", КолвоСтрок, КолвоКолонок);
Для Индекс = 0 По КолвоСтрок - 1 Цикл
Массив.SetValue(Индекс, 0, Данные[Индекс].Номенклатура);
Массив.SetValue(Индекс, 1, Данные[Индекс].Количество);
КонецЦикла;
Диапазон = Лист.Range(Лист.Cells(2, 1), Лист.Cells(КолвоСтрок + 1, КолвоКолонок));
Диапазон.Value = Массив;
Индексы COMSafeArray начинаются с нуля, а ячейки Excel — с единицы. Данные начинаются со второй строки, потому что первую занимает шапка. Отсюда КолвоСтрок + 1 в правой границе диапазона.
CSV для выгрузок «на миллион»
ЗаписьТекста не держит файл в памяти целиком, поэтому выгрузка таблицы в любой размер упирается только в саму таблицу значений. Если и она не помещается в память, выгружайте прямо из выборки запроса (Запрос.Выполнить().Выбрать()), без промежуточной таблицы значений.
Какой способ выгрузки таблицы значений в Excel выбрать
| Способ | Нужен Excel | Где работает | Скорость на 100 000 строк | Оформление | Когда выбирать |
|---|---|---|---|---|---|
| «Вывести список» | Нет | Любой клиент | Зависит от формы | Минимальное | Разовая выгрузка пользователем |
Построитель отчёта + Записать | Нет | Сервер | Хорошая | Своё, построителя | Быстрый код «лишь бы выгрузить» |
| Табличный документ вручную + XLSX | Нет | Сервер, клиент, Linux | Хорошая | Шрифты, ширина, типы ячеек | Основной вариант для обработок |
| COM по ячейкам | Да | Только Windows-клиент | Низкая | Любое: листы, формулы, фильтры | Небольшие таблицы по шаблону |
COM + COMSafeArray | Да | Только Windows-клиент | Высокая | Любое | Большие таблицы со сложным оформлением |
CSV через ЗаписьТекста | Нет | Сервер, клиент, Linux | Самая высокая | Никакого | Огромные объёмы, загрузка в другие системы |
Для обработки, которой будут пользоваться другие люди, выбирайте табличный документ и XLSX: он не зависит от установленного Office, работает в серверной базе на Linux и в веб-клиенте при передаче файла через временное хранилище.