Перенос серверной части платформы «1С:Предприятие 8» на операционные системы семейства Linux — стандартный шаг при построении отказоустойчивых и импортонезависимых ИТ-инфраструктур. Однако в процессе миграции разработчики часто сталкиваются со специфическим поведением платформы при работе с файлами. Типичный сценарий: код чтения файла Excel, который годами безупречно работал на Windows-сервере, при переносе на Linux начинает аварийно завершаться с ошибкой «Формат файла не поддерживается».
В этой статье мы подробно разберем, почему встроенный метод ТабличныйДокумент.Прочитать() может давать сбой на Linux-сервере, выявим скрытые причины этой проблемы и рассмотрим несколько альтернативных, абсолютно надежных способов чтения файлов XLSX в среде Linux.
Прежде чем настраивать операционную систему или переписывать код, необходимо исключить внутренние ошибки самой платформы «1С:Предприятие». В некоторых релизах ветки 8.3.16 (в частности, в версии 8.3.16.1063) присутствовал критический баг, из-за которого метод Прочитать() не мог распознать формат XLSX даже на операционной системе Windows.
Если вы столкнулись с этой ошибкой, первым делом проверьте версию вашей платформы. Переход на стабильные релизы веток 8.3.17, 8.3.18 и выше полностью решает эту внутреннюю проблему. Если же оперативное обновление платформы на сервере невозможно по регламентным причинам, вам придется использовать обходные пути чтения данных, которые мы рассмотрим ниже.
Разберем ситуацию, которая часто встречается при интеграции 1С со сторонними веб-сервисами или внешними учетными системами. Разработчики таких систем часто идут по пути наименьшего сопротивления: они формируют обычный текстовый файл в формате HTML-таблицы или CSV, но принудительно сохраняют его с расширением .xlsx.
Операционная система Windows и офисный пакет MS Excel крайне снисходительны к таким файлам. При открытии Excel анализирует внутреннюю структуру файла, понимает, что перед ним HTML, и молча визуализирует его для пользователя. Однако серверная часть 1С на Linux работает по строгим стандартам:
ТабличныйДокумент.Прочитать() получает на вход файл с расширением .xlsx, он ожидает увидеть там строго соответствующую спецификацию OpenXML (ZIP-архив с набором XML-схем).Для того чтобы обезопасить свой код от таких файлов, мы рекомендуем программно проверять реальную сигнатуру файла перед его чтением. Настоящий файл XLSX всегда является ZIP-архивом, а значит, его первые два байта должны содержать символы PK (шестнадцатеричные байты 50 4B). Посмотрим на пример функции проверки сигнатуры:
Функция ФайлЯвляетсяРеальнымZip(ПутьКФайлу) Экспорт
ФайлПроверки = Новый Файл(ПутьКФайлу);
Если Не ФайлПроверки.Существует() Тогда
Возврат Ложь;
КонецЕсли;
// Читаем первые два байта файла
Поток = Новый ЧтениеДанных(ПутьКФайлу);
Буфер = Поток.Прочитать(2);
Поток.Закрыть();
Если Буфер.Размер() < 2 Тогда
Возврат Ложь;
КонецЕсли;
// ASCII-коды для букв P (80) и K (75)
Возврат (Буфер[0] = 80 И Буфер[1] = 75);
КонецФункции
Если функция возвращает Ложь, значит, под маской XLSX скрывается текстовый файл или HTML-документ. В таком случае его нужно читать другими методами (например, через ЧтениеТекста или предварительно переименовав в .html и прочитав как HTML-документ).
Метод ТабличныйДокумент.Прочитать() не просто парсит данные из файла. Под капотом он строит полноценную графическую модель документа в оперативной памяти сервера: рассчитывает ширину колонок, высоту строк, переносит текст и применяет стили форматирования. Для этого платформе жизненно необходимы системные шрифты.
По умолчанию в дистрибутивах Linux (Ubuntu, Debian, RedHat) отсутствуют проприетарные шрифты Microsoft (Arial, Calibri, Times New Roman, Tahoma), которые чаще всего используются при создании документов Excel. Без этих шрифтов расчет графической сетки табличного документа может привести к фатальному сбою выполнения операции чтения.
Чтобы решить эту проблему, выполните установку необходимых пакетов на сервере Linux через консоль администрирования.
Для дистрибутивов Ubuntu/Debian выполните команды:
sudo apt-get update
sudo apt-get install ttf-mscorefonts-installer fontconfig
sudo fc-cache -f -v
Для дистрибутивов RedHat/CentOS/Astra Linux:
sudo yum install curl cabextract xorg-x11-font-utils fontconfig
sudo rpm -i https://downloads.sourceforge.net/project/mscorefonts2/rpms/msttcorefonts-db-1.5-1.noarch.rpm
sudo fc-cache -f -v
После установки шрифтов перезапустите службу сервера 1С:Предприятие (service srv1cv83 restart), чтобы платформа смогла корректно инициализировать новые шрифты в контексте процессов rphost.
Если вы хотите полностью застраховать свои фоновые задания от капризов платформы и системных библиотек рендеринга на Linux, лучшим решением станет разбор XLSX-файла как обычного ZIP-архива. Формат XLSX устроен прозрачно: это ZIP-архив, внутри которого лежат файлы XML. Основные данные хранятся в двух файлах:
xl/sharedStrings.xml — уникальный список всех текстовых строк, используемых в таблице.xl/worksheets/sheet1.xml — структура первой страницы книги со ссылками на индексы строк из sharedStrings и числовыми значениями ячеек.Рассмотрим пример кода, реализующий базовый алгоритм распаковки и чтения данных без использования табличного документа:
Функция ПрочитатьXLSXКакАрхив(ПутьКФайлу) Экспорт
ТаблицаРезультат = Новый ТаблицаЗначений;
// Создаем колонки под наши данные (например, три колонки)
ТаблицаРезультат.Колонки.Добавить("Колонка1");
ТаблицаРезультат.Колонки.Добавить("Колонка2");
ТаблицаРезультат.Колонки.Добавить("Колонка3");
// Создаем уникальный каталог во временной папке для распаковки
ПутьРаспаковки = КаталогВременныхФайлов() + "xlsx_unpacked_" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");
СоздатьКаталог(ПутьРаспаковки);
// Распаковываем архив средствами 1С
Архив = Новый ЧтениеZipФайла(ПутьКФайлу);
Архив.ИзвлечьВсе(ПутьРаспаковки, РежимВосстановленияПутейФайловZIP.Восстанавливать);
Архив.Закрыть();
// 1. Сначала считываем sharedStrings.xml, чтобы получить массив всех строк
МассивСтрок = Новый Массив;
ФайлСтрок = Новый Файл(ПутьРаспаковки + "/xl/sharedStrings.xml");
Если ФайлСтрок.Существует() Тогда
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ФайлСтрок.ПолноеИмя);
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "t" Тогда
МассивСтрок.Добавить(ЧтениеXML.ПрочитатьСтроку());
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
КонецЕсли;
// 2. Читаем структуру листа sheet1.xml
ФайлЛиста = Новый Файл(ПутьРаспаковки + "/xl/worksheets/sheet1.xml");
Если ФайлЛиста.Существует() Тогда
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ФайлЛиста.ПолноеИмя);
НоваяСтрока = Неопределено;
ТекущаяКолонка = 0;
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Если ЧтениеXML.Имя = "row" Тогда
НоваяСтрока = ТаблицаРезультат.Добавить();
ТекущаяКолонка = 0;
ИначеЕсли ЧтениеXML.Имя = "c" Тогда
// Определяем тип ячейки (s - shared string)
ТипЯчейки = ЧтениеXML.ПолучитьАтрибут("t");
// Переходим к значению ячейки
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "v" Тогда
ЗначениеЯчейки = ЧтениеXML.ПрочитатьСтроку();
Если ТипЯчейки = "s" Тогда
ИндексСтроки = Число(ЗначениеЯчейки);
ИтоговоеЗначение = МассивСтрок[ИндексСтроки];
Иначе
ИтоговоеЗначение = ЗначениеЯчейки;
КонецЕсли;
Если ТекущаяКолонка < ТаблицаРезультат.Колонки.Количество() Тогда
НоваяСтрока[ТекущаяКолонка] = ИтоговоеЗначение;
КонецЕсли;
ТекущаяКолонка = ТекущаяКолонка + 1;
Прервать;
КонецЕсли;
Если ЧтениеXML.ТипУзла = ТипУзлаXML.КонецЭлемента И ЧтениеXML.Имя = "c" Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
КонецЕсли;
// Удаляем временные файлы
УдалитьФайлы(ПутьРаспаковки);
Возврат ТаблицаРезультат;
КонецФункции
Этот метод работает на абсолютно любой версии платформы 1С, начиная с ранних релизов 8.3, не требует наличия установленных шрифтов в Linux, не зависит от ошибок рендеринга и выполняется в разы быстрее стандартного метода Прочитать() за счет отсутствия накладных расходов на визуальное построение табличной сетки.
В Linux-системах служба сервера 1С обычно запускается под учетной записью с ограниченными правами (например, usr1cv8). Некорректно настроенные права доступа на временные папки (такие как /tmp или /var/1C) часто приводят к ошибкам чтения и записи файлов.
Если ваша платформа обновлена до современных версий, мы рекомендуем вообще отказаться от сохранения промежуточных файлов на жесткий диск сервера. Используйте чтение данных непосредственно из оперативной памяти с помощью объектов ПотокВПамяти. Это полностью исключает дисковые коллизии и проблемы прав доступа в Linux.
Посмотрим, как реализовать этот подход:
// Предположим, что файл Excel передан с клиента в виде Двоичных Данных
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресВХранилище);
Поток = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(Поток);
ЗаписьДанных.Записать(ДвоичныеДанныеФайла);
ЗаписьДанных.Закрыть(); // Закрываем запись, чтобы сбросить буфер в поток
// Сбрасываем позицию чтения на начало потока
Поток.Перейти(0, ПозицияВПотоке.Начало);
ТабДок = Новый ТабличныйДокумент;
// Читаем документ напрямую из оперативной памяти
ТабДок.Прочитать(Поток, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Поток.Закрыть();
Для стабильной работы импорта Excel на сервере Linux необходимо придерживаться следующих правил:
8.3.17), чтобы избежать платформенных багов парсинга.PK, чтобы отсекать «лже-XLSX» документы (переименованные HTML или CSV).