При разработке обработок выгрузки данных в формат Excel программисты часто сталкиваются со странным поведением системы (даже используя экспорт справочников в Excel): даже если данных совсем немного (например, несколько сотен строк), 1С выдает критическую ошибку: «Документ Excel 2007 не может содержать более 16384 колонок и 1048576 строк». В этой статье мы подробно разберем, почему возникает это сообщение, и как правильно организовать выгрузку пакета документов, чтобы избежать подобных проблем.
Прежде всего, проанализируем природу сообщения. Текст ошибки часто вводит в заблуждение. На самом деле платформа 1С выдает это сообщение как «дежурное» при любом сбое в процессе формирования структуры .xlsx файла через объект ПакетОтображаемыхДокументов. Основные причины могут быть следующими:
\ / ? * [ ] :). Если имя сформировано некорректно, 1С может выдать ошибку о превышении лимитов строк/колонок вместо сообщения о неверном имени листа.Записать().Для стабильной работы выгрузки мы должны придерживаться правила: формирование файла должно происходить на сервере (желательно используя фоновое задание с индикацией), а на клиент должен возвращаться уже готовый «слепок» файла в виде двоичных данных. Рассмотрим этот процесс по шагам.
В исходном (проблемном) коде разработчик пытался собрать пакет на клиенте, передавая адреса из временного хранилища. Это заставляло клиентское приложение выполнять тяжелую работу по сборке ZIP-архива (которым является XLSX). Правильный подход — сделать это на сервере.
Создадим функцию, которая будет собирать все листы (аналогичный подход применяется, когда нужна выгрузка журнала регистрации в Excel) в один объект ПакетОтображаемыхДокументов и сохранять его во временный файл прямо на сервере.
&НаСервере
Функция СформироватьФайлНаСервере()
// Создаем объект для объединения листов
Пакет = Новый ПакетОтображаемыхДокументов;
// Допустим, у нас есть массив структур с данными листов
// Каждый элемент содержит ИмяЛиста и ТабДок (или адрес в хранилище)
Для Каждого ДанныеЛиста Из МассивДанныхДляВыгрузки Цикл
// Получаем табличный документ из временного хранилища
ТабДок = ПолучитьИзВременногоХранилища(ДанныеЛиста.Адрес);
// Добавляем новый элемент в пакет
ЭлементПакета = Пакет.Состав.Добавить();
// Важно: проверяем имя листа на соответствие требованиям Excel
ИмяЛиста = ДанныеЛиста.Наименование;
ИмяЛиста = СтрЗаменить(ИмяЛиста, "/", " "); // Удаляем запрещенные символы
ЭлементПакета.Наименование = Лев(ИмяЛиста, 31); // Ограничиваем длину
ЭлементПакета.Данные = ДанныеЛиста.Адрес; // Передаем адрес документа
КонецЦикла;
// Генерируем уникальное имя файла на сервере
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
Попытка
// Записываем пакет в файл в формате XLSX
Пакет.Записать(ИмяВременногоФайла, ТипФайлаПакетаОтображаемыхДокументов.XLSX);
// Читаем файл в двоичные данные для передачи на клиент
ДанныеФайла = Новый ДвоичныеДанные(ИмяВременногоФайла);
// Удаляем временный файл с диска сервера
УдалитьФайлы(ИмяВременногоФайла);
Возврат ДанныеФайла;
Исключение
ЗаписьЖурналаРегистрации("ВыгрузкаВExcel", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
ВызватьИсключение "Не удалось сформировать файл на сервере. Подробности в журнале регистрации.";
КонецПопытки;
КонецФункции
Теперь на стороне клиента нам остается только вызвать серверную функцию, получить ДвоичныеДанные и сохранить их на диск пользователя (см. также копирование файлов между клиентом и сервером). Этот метод максимально эффективен, так как клиент не занимается обработкой структуры таблиц, а просто пишет поток байтов на диск.
&НаКлиенте
Процедура ВыполнитьВыгрузку(Команда)
// Вызываем серверную функцию формирования
ДанныеФайла = СформироватьФайлНаСервере();
Если ДанныеФайла = Неопределено Тогда
Возврат;
КонецЕсли;
// Определяем путь сохранения
ПутьКФайлу = "C:\Выгрузки\ОтчетПоОрганизациям.xlsx";
Попытка
// Метод Записать() двоичных данных просто сохраняет их как файл
ДанныеФайла.Записать(ПутьКФайлу);
Сообщить("Файл успешно выгружен: " + ПутьКФайлу);
Исключение
Сообщить("Ошибка при сохранении файла на диск: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Проанализируем несколько технических деталей, которые помогут сделать вашу выгрузку еще надежнее:
ПоместитьВоВременноеХранилище), всегда привязывайте их к уникальному идентификатору формы. Это гарантирует, что данные не будут удалены «сборщиком мусора» до того, как сервер закончит формирование пакета.ТабДок.ВысотаТаблицы). Если документ пуст, Excel может выдать ошибку при попытке открыть такой файл.ТабДок.АвтоМасштаб = Истина. При упаковке в пакет это свойство сохранится для каждого листа.Рассмотрим ситуацию, когда имен листов много и они могут дублироваться. Excel не допускает одинаковых имен вкладок. Перед вставкой в ЭлементПакета.Наименование рекомендуем использовать вспомогательную функцию для проверки уникальности имен в рамках одного пакета.
Если вы столкнулись с ошибкой о превышении колонок и строк при использовании ПакетОтображаемыхДокументов, не ищите лишние строки в коде. Скорее всего, проблема в контексте исполнения. Перенос логики с клиента на сервер и использование ДвоичныеДанные для передачи готового файла — это стандарт «золотого сечения» в разработке на платформе 1С 8.3. Это снимает ограничения по памяти, обходит баги десериализации и обеспечивает стабильную работу даже при формировании очень тяжелых отчетов.