При разработке обработок загрузки данных (например, XML, Excel или CSV) разработчики часто сталкиваются с ситуацией, когда на локальной файловой базе код работает корректно, а в клиент-серверном варианте при запуске через тонкий клиент возникает ошибка: «Файл не найден по указанному пути».
Выясним причину такого поведения и разберем по шагам правильные архитектурные подходы к передаче и обработке файлов в управляемом приложении.
Проанализируем ситуацию: когда процедура с директивой компиляции &НаСервере пытается открыть файл по переданной строке пути (например, C:\Users\User\Desktop\data.xml), она обращается к файловой системе компьютера, на котором физически работает служба сервера 1С:Предприятия.
Сервер не имеет прямого доступа к локальному жесткому диску клиентской рабочей станции. Поэтому просто передать путь к клиентскому файлу в серверный контекст нельзя. Чтобы сервер смог обработать данные, сам файл необходимо предварительно передать на сторону сервера.
Рассмотрим основные способы реализации такой передачи.
Этот способ является классическим для платформы 1С:Предприятие 8.3 при работе в асинхронном режиме интерфейса. Файл с клиентского компьютера помещается во временное хранилище сеанса, после чего на сервере мы извлекаем его и сохраняем во временный каталог для чтения.
Посмотрим на пример реализации:
&НаКлиенте
Процедура ВыбратьИЗагрузитьФайл(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Файлы XML (*.xml)|*.xml";
Диалог.Заголовок = "Выберите файл для загрузки";
Диалог.ПолноеИмяФайла = Объект.ИмяФайла;
Диалог.ПроверятьСуществованиеФайла = Истина;
// Помещаем файл во временное хранилище формы
НачатьПомещениеФайлов(Оповещение, , Диалог, Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораФайла(ПомещенныеФайлы, ДополнительныеПараметры) Экспорт
Если ПомещенныеФайлы = Неопределено Или ПомещенныеФайлы.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Получаем адрес во временном хранилище
АдресХранилища = ПомещенныеФайлы[0].Хранение;
Объект.ИмяФайла = ПомещенныеФайлы[0].Имя;
// Вызываем серверную обработку
ПрочитатьФайлНаСервере(АдресХранилища);
КонецПроцедуры
&НаСервере
Процедура ПрочитатьФайлНаСервере(АдресХранилища)
// Получаем двоичные данные из временного хранилища
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
Если ДвоичныеДанные = Неопределено Тогда
ВызватьИсключение "Не удалось получить данные файла из хранилища.";
КонецЕсли;
// Создаем временный файл на диске сервера
ВременныйПуть = ПолучитьИмяВременногоФайла("xml");
ДвоичныеДанные.Записать(ВременныйПуть);
Попытка
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ВременныйПуть);
// Выполняем разбор структуры XML
Пока Чтение.Прочитать() Цикл
// Логика чтения узлов
КонецЦикла;
Чтение.Закрыть();
Исключение
УдалитьФайлы(ВременныйПуть);
ВызватьИсключение;
КонецПопытки;
// Обязательно удаляем временный файл после окончания работы
УдалитьФайлы(ВременныйПуть);
КонецПроцедуры
Объект ДвоичныеДанные сериализуется между контекстами клиента и сервера. Если файл уже выбран или получен на клиенте, мы можем инициализировать ДвоичныеДанные прямо на клиенте и передать их в качестве аргумента процедуры &НаСервере.
Кроме того, для чтения XML нет необходимости обязательно сохранять файл на серверный диск: можно открыть поток чтения в памяти с помощью метода ОткрытьПоток объекта ЧтениеXML.
Разберем подробнее этот сценарий:
&НаКлиенте
Процедура ЗагрузитьНапрямую(Команда)
// Считываем файл в двоичные данные на клиенте
ДанныеФайла = Новый ДвоичныеДанные(Объект.ПутьКФайлуНаКлиенте);
// Передаем объект в серверную процедуру
ОбработатьДанныеНаСервере(ДанныеФайла);
КонецПроцедуры
&НаСервере
Процедура ОбработатьДанныеНаСервере(ДанныеФайла)
// Открываем поток для чтения напрямую из памяти без записи на диск
Поток = ДанныеФайла.ОткрытьПотокДляЧтения();
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьПоток(Поток);
Пока Чтение.Прочитать() Цикл
// Обработка данных XML
КонецЦикла;
Чтение.Закрыть();
Поток.Закрыть();
КонецПроцедуры
Начиная с современных версий платформы 8.3, для повышения читаемости кода рекомендуется использовать асинхронные методы и оператор Ждать вместо создания промежуточных процедур оповещения:
&НаКлиенте
Асинх Процедура ВыбратьИЗагрузитьАсинхронно(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Файлы XML (*.xml)|*.xml";
ВыбранныеФайлы = Ждать Диалог.ВыбратьАсинх();
Если ВыбранныеФайлы = Неопределено Тогда
Возврат;
КонецЕсли;
ПутьКФайлу = ВыбранныеФайлы[0];
// Помещаем файл во временное хранилище асинхронно
РезультатПомещения = Ждать ПоместитьФайлНаСерверАсинхрон(,,, ПутьКФайлу, УникальныйИдентификатор);
Если РезультатПомещения <> Неопределено И РезультатПомещения.ПомещениеФайлаОтменено = Ложь Тогда
ПрочитатьФайлНаСервере(РезультатПомещения.Адрес);
КонецЕсли;
КонецПроцедуры
ПолучитьИмяВременногоФайла() и метод Записать(), всегда оборачивайте логику чтения в блок Попытка ... Исключение и удаляйте временный файл методом УдалитьФайлы(), чтобы избежать засорения системного диска сервера.УникальныйИдентификатор текущей формы. Это гарантирует, что данные во временном хранилище будут существовать на протяжении всей жизни формы и не удалятся после первого серверного вызова.\\server\share\file.xml), служба сервера 1С сможет прочитать его напрямую без предварительной передачи только в том случае, если учетная запись операционной системы, под которой запущена служба ragent/rphost (например, USR1CV8), имеет полные права доступа к этой сетевой папке.