При переносе или разработке механизмов загрузки данных из электронных таблиц на сервер под управлением Linux (например, РЕД ОС, Astra Linux или Ubuntu) разработчики часто сталкиваются с неочевидным поведением платформы. Метод ТабличныйДокумент.Прочитать() при попытке загрузить временный файл с расширением .xls неожиданно падает с исключением: «Ошибка при выполнении файловой операции. Доступ к файлу не может быть получен».
При этом сам файл физически существует на диске, доступен процессу сервера 1С на чтение и запись, а права доступа настроены корректно. Разберем по шагам, почему возникает эта ошибка, как провести точную диагностику и как гарантированно решить проблему.
Рассмотрим стандартный сценарий, при котором возникает сбой. Пользователь передает файл с клиента во временное хранилище, а на сервере выполняется следующая процедура:
Функция ЗагрузитьКаталог(АдресКФайлу, Расширение = ".xls") Экспорт
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресКФайлу);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(Расширение);
ДвоичныеДанные.Записать(ИмяВременногоФайла);
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
// На строке Прочитать возникает ошибка файловой операции
КонецФункции
Проанализируем ситуацию: системная проверка прав в каталоге /tmp показывает, что владелец файла — пользователь службы 1С (например, usr1cv8:grp1cv8) с правами -rw-r-----. Более того, файл успешно копируется средствами встроенного языка с помощью функции КопироватьФайл(). Это доказывает, что на уровне операционной системы никаких проблем с чтением нет.
Выясним причину такого поведения. Формулировка ошибки «Доступ к файлу не может быть получен» в данном контексте вводит разработчика в заблуждение. Она генерируется не операционной системой, а внутренним парсером платформы 1С.
В операционной системе Windows настольное приложение MS Excel или сторонние компоненты легко «прощают» ошибки в структуре: например, когда текстовый документ с HTML-разметкой (теги <table>, <tr>, <td>) или XML Spreadsheet 2003 сохраняется с расширением .xls. Excel при открытии такого документа может выдать предупреждение: «Действительный формат открываемого файла отличается от указываемого его расширением имени файла», но все равно отобразит таблицу.
На сервере Linux платформа 1С использует встроенную строгую библиотеку для работы с форматом Excel 97–2003 (бинарный формат BIFF8 / Compound File Binary Format). Когда платформе передается файл с расширением .xls, парсер ожидает увидеть бинарный контейнер с сигнатурой OLE. Если внутри оказывается HTML-текст, XML или поврежденный заголовок, библиотека разбора не может инициализировать поток внутри составного файла и возвращает низкоуровневую ошибку, которую 1С транслирует как ошибку доступа к файлу.
Посмотрим на альтернативный синтаксис метода ТабличныйДокумент.Прочитать(). Платформа 1С поддерживает чтение напрямую из объекта ПотокВПамяти без сохранения файла на диск. Однако здесь действует жесткое системное ограничение: чтение из потока поддерживается только для форматов MXL, MXL7 и ODS. Чтение форматов XLS и XLSX через поток не поддерживается — платформа всегда требует передачи пути к физическому файлу на диске.
Чтобы убедиться в том, что проблема кроется в формате файла, выполним следующие шаги:
file /путь/к_файлу.xls. Если утилита возвращает HTML document text, XML document text или ASCII text вместо Composite Document File V2 Document, значит, файл не является подлинным бинарным XLS.ЧтениеДанных.Посмотрим на пример кода, позволяющего определить реальный тип поступившего документа:
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресКФайлу);
Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения();
Чтение = Новый ЧтениеДанных(Поток);
// Считываем первые 8 байт сигнатуры
БайтыСигнатуры = Чтение.ПрочитатьДвоичныеДанные(8);
Поток.Закрыть();
// У настоящего XLS (OLE Compound Document) первые байты: D0 CF 11 E0 A1 B1 1A E1
// У файлов XLSX (ZIP-архив) первые байты: 50 4B 03 04 (PK..)
Рассмотрим практические варианты исправления ошибки в зависимости от источника формирования файла.
Формат .xlsx (OpenXML) является стандартом де-факто. Библиотеки разбора формата XLSX в платформе 1С работают гораздо стабильнее и предсказуемее на всех операционных системах. Если файл выгружается контрагентом или внешней учетной системой, необходимо перенастроить выгрузку в формат XLSX. При сохранении файла на сервере достаточно указать правильное расширение:
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(".xlsx");
ДвоичныеДанные.Записать(ИмяВременногоФайла);
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Если поставщик данных формирует под видом XLS обычную HTML-таблицу и изменить формат на источнике невозможно, читать такой файл как табличный документ нельзя. В этом случае разберем его как текст:
// Если сигнатура не совпадает с бинарным XLS, читаем как HTML
ЧтениеТекста = Новый ЧтениеТекста(ИмяВременногоФайла, КодировкаТекста.UTF8);
СодержимоеHTML = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();
// Для разбора структуры тегов tr и td можно использовать ЧтениеXML
// предварительно подготовив валидный XHTML-контекст, либо разобрать строки
Обратим внимание на важную особенность файловых систем Linux: они чувствительны к регистру символов. Если функция ПолучитьИмяВременногоФайла() принимает расширение в верхнем регистре (например, .XLS), а последующий модуль пытается обращаться к файлу с расширением .xls, операция завершится ошибкой отсутствия доступа или ненахождения файла. Всегда передавайте расширение в едином нижнем регистре: .xls или .xlsx.
Также перед выполнением чтения рекомендуется проверять существование записанного файла на диске с помощью объекта Файл:
ДвоичныеДанные.Записать(ИмяВременногоФайла);
ФайлПроверки = Новый Файл(ИмяВременногоФайла);
Если ФайлПроверки.Существует() Тогда
ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
КонецЕсли;
Применение строгого контроля за фактическим содержимым файлов и переход на формат XLSX позволяют полностью исключить ложные ошибки доступа при работе 1С на серверах под управлением Linux.