При разработке на платформе 1С в клиент-серверном режиме (поможет универсальная консоль кода и инструменты отладки) часто встречается ситуация, когда код, прекрасно работавший в файловой базе, внезапно перестает функционировать и выдает ошибку. Одна из таких классических проблем — ошибка при чтении файла с компьютера пользователя на сервере. Рассмотрим подробно, почему это происходит и как правильно организовать работу с файлами в такой архитектуре.
Типичная ошибка выглядит так:
{Форма.Форма.Форма(105)}: Ошибка при вызове метода контекста (Прочитать)
ТабДок.Прочитать(ПолноеИмяФайла);
по причине:
Каталог не обнаружен 'C:\Documents and Settings\Owner\Desktop\Таблица.mxl'
Давайте вместе разберемся в корне этой проблемы и научимся ее решать.
Чтобы понять, почему возникает ошибка, нужно четко представлять себе, как работает клиент-серверная архитектура 1С. В этом режиме есть как минимум две разные машины (или два разных процесса):
&НаКлиенте выполняется именно здесь.rphost.exe), который работает на серверной машине. Он выполняет основную бизнес-логику, работает с базой данных и исполняет код с директивой &НаСервере.Когда вы пытаетесь выполнить код ТабДок.Прочитать("C:\Desktop\file.mxl") в процедуре, выполняемой &НаСервере, сервер 1С пытается найти этот файл на своем собственном диске C:. Естественно, на сервере нет рабочего стола вашего пользователя, поэтому он и сообщает, что "Каталог не обнаружен".
Важный момент: Серверный процесс 1С обычно запускается от имени специального системного пользователя (например, USR1CV8). У этого пользователя нет доступа к файлам на рабочих столах других пользователей, даже если они работают на той же машине, и он не видит подключенные сетевые диски.
Таким образом, прямое чтение файла с компьютера пользователя в серверном коде невозможно. Файл нужно сначала передать с клиента на сервер.
Для корректного обмена файлами между клиентом и сервером в платформе 1С существует специальный механизм — временное хранилище. Алгоритм действий должен быть следующим:
ТабличныйДокумент.Прочитать() успешно читает этот временный файл — для этого подойдёт импорт данных из Excel в справочники 1С.Посмотрим на практические примеры реализации этого алгоритма.
Этот подход является базовым и хорошо иллюстрирует механику процесса. Он использует синхронный вызов, который "подвешивает" интерфейс на время передачи файла.
Сначала на клиенте организуем выбор файла и его помещение во временное хранилище.
&НаКлиенте
Процедура ДобавитьМакетИзФайла(Команда)
// Здесь должен быть код для выбора файла пользователем,
// в результате которого мы получаем ПолноеИмяФайла.
// Например, через ДиалогВыбораФайла.
// ...
Адрес = "";
// Помещаем файл во временное хранилище и получаем его адрес
Если ПоместитьФайл(Адрес, ПолноеИмяФайла, , Ложь) Тогда
// Вызываем серверную процедуру и передаем ей адрес
ЗагрузитьТабДокИзФайлаНаСервере(Адрес);
КонецЕсли;
КонецПроцедуры
Теперь реализуем серверную часть, которая получит файл и прочитает его.
&НаСервере
Процедура ЗагрузитьТабДокИзФайлаНаСервере(Адрес)
// Получаем имя для нового временного файла на сервере
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(".mxl");
// Получаем двоичные данные из хранилища по адресу
ДвоичныеДанные = ПолучитьИзВременногоХранилища(Адрес);
// Записываем данные во временный файл на диске сервера
ДвоичныеДанные.Записать(ИмяВременногоФайла);
// Теперь читаем файл, который гарантированно доступен на сервере
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяВременногоФайла);
// Не забываем очистить за собой
УдалитьФайлы(ИмяВременногоФайла);
// ... дальнейшая обработка ТабДок ...
КонецПроцедуры
Этот код надежно решает проблему доступа. Сервер больше не пытается найти файл на клиентской машине.
В современных приложениях, особенно при работе в веб-клиенте, синхронные операции с файлами нежелательны или запрещены. Они блокируют интерфейс. Для этого в платформе существуют асинхронные методы.
Рассмотрим пример с использованием НачатьПомещениеФайла и механизма оповещений.
&НаКлиенте
Процедура ЗагрузитьИзФайлаАсинхронно(Команда)
// Создаем описание оповещения.
// Указываем процедуру, которая выполнится после выбора и помещения файла.
Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтаФорма);
// Запускаем асинхронный процесс выбора и помещения файла
НачатьПомещениеФайла(Оповещение, , , Истина);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
// Эта процедура будет вызвана платформой автоматически
Если Результат Тогда
// Файл успешно выбран и помещен, у нас есть его адрес в хранилище
ЗагрузитьНаСервере(Адрес);
Иначе
// Пользователь отменил выбор файла
Сообщить("Выбор файла был отменен.");
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьНаСервере(Адрес)
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(".xlsx");
ДвоичныеДанные = ПолучитьИзВременногоХранилища(Адрес);
ДвоичныеДанные.Записать(ИмяВременногоФайла);
ЭтотОбъект.ТабДок.Прочитать(ИмяВременногоФайла); // Предполагаем, что ТабДок - реквизит формы
УдалитьФайлы(ИмяВременногоФайла);
КонецПроцедуры
Этот метод предпочтительнее, так как он не блокирует работу пользователя и является стандартом для веб-ориентированных приложений.
Даже при правильной передаче файла на сервер можно столкнуться с другими проблемами. Проанализируем самые распространенные из них.
Частая ошибка — пытаться прочитать файл одного формата, сохранив его с другим расширением. Например, если вы загружаете файл .xlsx, а во временный файл сохраняете с расширением .xls, метод Прочитать() может выдать ошибку. Всегда указывайте правильное расширение в функции ПолучитьИмяВременногоФайла(), соответствующее реальному типу файла.
Иногда ошибка чтения может возникнуть уже на сервере. Например, "Ошибка при выполнении файловой операции". Это может означать, что у пользователя, от имени которого запущен сервер 1С (USR1CV8), нет прав на запись во временную папку (temp) операционной системы. Это редкая ситуация, но ее стоит проверить системному администратору.
Если ваш сервер 1С работает под управлением Linux, помните — решается через массовая загрузка данных из Excel на Linux.
Метод ТабличныйДокумент.Прочитать() загружает весь файл целиком в оперативную память сервера. При работе с очень большими файлами (десятки и сотни мегабайт) это может привести к чрезмерному потреблению памяти процессом rphost и его аварийному завершению. Для обработки больших объемов данных рассмотрите альтернативные, потоковые методы чтения, например, объект ЧтениеДанныхXLSX (доступен в новых версиях платформы).
Таким образом, ключ к успешной работе с файлами в клиент-серверном режиме — это понимание разделения контекстов выполнения и использование штатных механизмов платформы для обмена данными между ними.