При разработке в среде 1С:Предприятие 8.3, особенно на управляемых формах, мы часто сталкиваемся с задачей загрузки данных из Excel, XML или текстовых документов. Казалось бы, задача тривиальная: пользователь выбирает файл, нажимает кнопку, и система его обрабатывает. Часто такой импорт данных необходим для первоначального заполнения базы из файлов различных форматов (*.xls, *.ods, *.dbf и др.). Однако, как только мы переносим выполнение кода на сервер (что является стандартом для выполнения "тяжелых" операций), возникает ошибка: «Файл не обнаружен» или «Ошибка доступа к файлу».
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем архитектурные ограничения платформы и научимся правильно передавать файлы с компьютера пользователя на сервер 1С, используя современные методы разработки и передачи файлов.
Давайте проанализируем ситуацию. Когда мы пишем код, который работает в файловом варианте на нашем локальном компьютере, путь к файлу вида C:\Users\User\Documents\Report.xlsx доступен и клиентскому приложению, и "серверной" части (которая в файловом варианте эмулируется на той же машине).
Однако в реальной клиент-серверной архитектуре ситуация меняется кардинально:
Когда мы передаем строковый путь к файлу с клиента на сервер, сервер 1С пытается найти этот путь на своих локальных дисках. Естественно, на сервере нет папки конкретного пользователя, и доступа к диску C: компьютера пользователя у него тоже нет. Если вам требуется прямое копирование файлов и каталогов между Клиентом и Сервером, необходимо использовать специализированные механизмы платформы.
Чтобы решить эту задачу, нам необходимо физически "переправить" содержимое файла (его двоичные данные) с клиента на сервер. В платформе 1С для этого существует специальный механизм — Временное хранилище.
Рассмотрим алгоритм действий, который мы будем реализовывать:
e1cib/tempstorage/UUID).В современных версиях платформы 1С (начиная с 8.3.15+) рекомендуется использовать асинхронные методы работы с файлами. Для быстрой реализации можно взять за основу шаблон простой обработки загрузки из excel, который наглядно демонстрирует этот подход — для этого подойдёт универсальный импорт данных из Excel в 1С. Рассмотрим пример кода, который инициирует выбор файла и его помещение в хранилище.
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
// Описываем параметры диалога выбора файла
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
ПараметрыДиалога.Заголовок = "Выберите файл для загрузки";
ПараметрыДиалога.Фильтр = "Файлы Excel (*.xlsx)|*.xlsx|Все файлы (*.*)|*.*";
// Описываем, какая процедура выполнится после выбора файла
ОповещениеЗавершения = Новый ОписаниеОповещения("ЗагрузитьФайлЗавершение", ЭтотОбъект);
// Запускаем процесс. УникальныйИдентификатор формы нужен, чтобы
// временное хранилище очистилось автоматически при закрытии формы.
НачатьПомещениеФайлаНаСервер(ОповещениеЗавершения, , , , ПараметрыДиалога, УникальныйИдентификатор);
КонецПроцедуры
Теперь разберем процедура-обработчик, которая сработает после того, как пользователь выберет файл (или откажется).
&НаКлиенте
Процедура ЗагрузитьФайлЗавершение(ОписаниеПомещенногоФайла, ДополнительныеПараметры) Экспорт
Если ОписаниеПомещенногоФайла = Неопределено Тогда
// Пользователь отказался от выбора
Возврат;
КонецЕсли;
// Если мы здесь, файл успешно улетел во временное хранилище.
// Адрес файла находится в свойстве Адрес
АдресВоВременномХранилище = ОписаниеПомещенногоФайла.Адрес;
ИмяФайла = ОписаниеПомещенногоФайла.СсылкаНаФайл.Имя;
// Теперь вызываем серверную процедуру и передаем туда Адрес
ОбработатьФайлНаСервере(АдресВоВременномХранилище, ИмяФайла);
КонецПроцедуры
Теперь переместимся в серверный контекст. Здесь наша задача — забрать данные из временного хранилища. Мы будем использовать метод ПолучитьИзВременногоХранилища. Подобным образом работает любая универсальная загрузка из Excel, позволяя импортировать данные в справочники «одним нажатием» — для этого есть универсальная загрузка документов из Excel и XML.
Важный момент: метод ПолучитьИзВременногоХранилища возвращает данные разного типа в зависимости от того, что туда положили. В нашем случае это будут ДвоичныеДанные. Чтобы с ними работать (например, прочитать Excel), нам часто нужно сохранить их во временный файл уже на диске сервера.
Посмотрим, как это реализуется:
&НаСервере
Процедура ОбработатьФайлНаСервере(АдресХранилища, ИмяФайла)
// 1. Получаем двоичные данные из хранилища
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
Если ДвоичныеДанные = Неопределено Тогда
Сообщить("Ошибка получения файла из хранилища!");
Возврат;
КонецЕсли;
// 2. Создаем временный файл на диске СЕРВЕРА.
// Серверу нужны права на запись в папку временных файлов (обычно они есть).
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
// 3. Записываем двоичные данные на диск сервера
ДвоичныеДанные.Записать(ИмяВременногоФайла);
Попытка
// Теперь мы можем работать с файлом по локальному пути сервера
// Например, откроем его через ТабличныйДокумент
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
// Здесь размещаем логику парсинга файла
// ...
Сообщить("Файл успешно прочитан на сервере. Строк: " + ТабДок.ВысотаТаблицы);
Исключение
Сообщить("Не удалось прочитать файл: " + ОписаниеОшибки());
КонецПопытки;
// 4. Хорошим тоном считается удаление временного файла после работы
УдалитьФайлы(ИмяВременногоФайла);
КонецПроцедуры
Давайте проанализируем важные детали, которые часто упускают при реализации этого механизма:
Обратите внимание на использование УникальныйИдентификатор при помещении файла. Если вы не укажете его, файл поместится во временное хранилище, но может быть удален системой сразу после окончания серверного вызова. Привязка к форме гарантирует, что файл "живет" пока открыта форма или пока мы явно его не удалим.
Если длительная обработка файла занимает много времени, ее лучше выносить в фоновое задание. В этом случае адрес временного хранилища нужно передавать в параметры фонового задания. Важно помнить: фоновое задание не имеет доступа к контексту формы, поэтому данные должны быть переданы именно через хранилище.
При использовании ПолучитьИмяВременногоФайла желательно указывать правильное расширение (в примере мы указали "xlsx"). Некоторые методы чтения (например, ТабличныйДокумент.Прочитать) могут ориентироваться на расширение файла для определения формата.
При использовании методов, работающих с внешними ресурсами (например, COM-объекты Excel на сервере), убедитесь, что настроены права доступа в 1С:Предприятие для пользователя, под которым запущена служба сервера 1С (обычно USR1CV8), включая доступ к временным папкам и права DCOM.
Иногда сохранять файл на диск сервера необязательно. Если вы работаете с текстовыми файлами или XML, объект ЧтениеТекста или ЧтениеXML может принимать поток данных напрямую.
Однако, если вы используете объект ДвоичныеДанные для передачи картинки или PDF, вы можете сразу сохранять их в реквизит справочника типа ХранилищеЗначения, минуя стадию записи во временный файл на диске:
// Пример сохранения в базу без промежуточного файла на диске
НовыйОбъект = Справочники.Файлы.СоздатьЭлемент();
НовыйОбъект.Имя = ИмяФайла;
// Создаем ХранилищеЗначения напрямую из ДвоичныхДанных
НовыйОбъект.ДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанные);
НовыйОбъект.Записать();
Мы подробно рассмотрели, как преодолеть барьер между файловой системой клиента и сервера. Используя механизм временного хранилища, вы обеспечиваете надежную передачу данных независимо от того, где находится пользователь — в соседней комнате или работает через веб-браузер с другого конца света.
Основные выводы:
НачатьПомещениеФайла... для лучшего пользовательского опыта.