Как правильно передать файл с клиента на сервер для обработки и избежать ошибки доступа?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке в среде 1С:Предприятие 8.3, особенно на управляемых формах, мы часто сталкиваемся с задачей загрузки данных из Excel, XML или текстовых документов. Казалось бы, задача тривиальная: пользователь выбирает файл, нажимает кнопку, и система его обрабатывает. Часто такой импорт данных необходим для первоначального заполнения базы из файлов различных форматов (*.xls, *.ods, *.dbf и др.). Однако, как только мы переносим выполнение кода на сервер (что является стандартом для выполнения "тяжелых" операций), возникает ошибка: «Файл не обнаружен» или «Ошибка доступа к файлу».

В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем архитектурные ограничения платформы и научимся правильно передавать файлы с компьютера пользователя на сервер 1С, используя современные методы разработки и передачи файлов.

Суть проблемы: Клиент и Сервер — это разные компьютеры

Давайте проанализируем ситуацию. Когда мы пишем код, который работает в файловом варианте на нашем локальном компьютере, путь к файлу вида C:\Users\User\Documents\Report.xlsx доступен и клиентскому приложению, и "серверной" части (которая в файловом варианте эмулируется на той же машине).

Однако в реальной клиент-серверной архитектуре ситуация меняется кардинально:

  1. Клиент — это компьютер пользователя, где запущен тонкий клиент или веб-клиент. Файл физически находится здесь.
  2. Сервер — это удаленный компьютер (кластер серверов 1С), где выполняется основной программный код.

Когда мы передаем строковый путь к файлу с клиента на сервер, сервер 1С пытается найти этот путь на своих локальных дисках. Естественно, на сервере нет папки конкретного пользователя, и доступа к диску C: компьютера пользователя у него тоже нет. Если вам требуется прямое копирование файлов и каталогов между Клиентом и Сервером, необходимо использовать специализированные механизмы платформы.

Решение проблемы: Временное хранилище

Чтобы решить эту задачу, нам необходимо физически "переправить" содержимое файла (его двоичные данные) с клиента на сервер. В платформе 1С для этого существует специальный механизм — Временное хранилище.

Рассмотрим алгоритм действий, который мы будем реализовывать:

  1. На клиенте пользователь выбирает файл.
  2. Мы считываем этот файл в двоичные данные или сразу помещаем его во временное хранилище.
  3. Система возвращает нам уникальный адрес (URL) этого файла во временном хранилище (например, e1cib/tempstorage/UUID).
  4. Мы передаем на сервер не путь к файлу на диске, а этот адрес.
  5. На сервере мы обращаемся к временному хранилищу по адресу и получаем данные файла для обработки.

Шаг 1: Выбор файла и помещение в хранилище (На Клиенте)

В современных версиях платформы 1С (начиная с 8.3.15+) рекомендуется использовать асинхронные методы работы с файлами. Для быстрой реализации можно взять за основу шаблон простой обработки загрузки из excel, который наглядно демонстрирует этот подход — для этого подойдёт универсальный импорт данных из Excel в 1С. Рассмотрим пример кода, который инициирует выбор файла и его помещение в хранилище.


&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
    
    // Описываем параметры диалога выбора файла
    ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
    ПараметрыДиалога.Заголовок = "Выберите файл для загрузки";
    ПараметрыДиалога.Фильтр = "Файлы Excel (*.xlsx)|*.xlsx|Все файлы (*.*)|*.*";
    
    // Описываем, какая процедура выполнится после выбора файла
    ОповещениеЗавершения = Новый ОписаниеОповещения("ЗагрузитьФайлЗавершение", ЭтотОбъект);
    
    // Запускаем процесс. УникальныйИдентификатор формы нужен, чтобы
    // временное хранилище очистилось автоматически при закрытии формы.
    НачатьПомещениеФайлаНаСервер(ОповещениеЗавершения, , , , ПараметрыДиалога, УникальныйИдентификатор);

КонецПроцедуры

Теперь разберем процедура-обработчик, которая сработает после того, как пользователь выберет файл (или откажется).


&НаКлиенте
Процедура ЗагрузитьФайлЗавершение(ОписаниеПомещенногоФайла, ДополнительныеПараметры) Экспорт
    
    Если ОписаниеПомещенногоФайла = Неопределено Тогда
        // Пользователь отказался от выбора
        Возврат;
    КонецЕсли;
    
    // Если мы здесь, файл успешно улетел во временное хранилище.
    // Адрес файла находится в свойстве Адрес
    АдресВоВременномХранилище = ОписаниеПомещенногоФайла.Адрес;
    ИмяФайла = ОписаниеПомещенногоФайла.СсылкаНаФайл.Имя;
    
    // Теперь вызываем серверную процедуру и передаем туда Адрес
    ОбработатьФайлНаСервере(АдресВоВременномХранилище, ИмяФайла);
    
КонецПроцедуры

Шаг 2: Получение и обработка файла (На Сервере)

Теперь переместимся в серверный контекст. Здесь наша задача — забрать данные из временного хранилища. Мы будем использовать метод ПолучитьИзВременногоХранилища. Подобным образом работает любая универсальная загрузка из Excel, позволяя импортировать данные в справочники «одним нажатием» — для этого есть универсальная загрузка документов из Excel и XML.

Важный момент: метод ПолучитьИзВременногоХранилища возвращает данные разного типа в зависимости от того, что туда положили. В нашем случае это будут ДвоичныеДанные. Чтобы с ними работать (например, прочитать Excel), нам часто нужно сохранить их во временный файл уже на диске сервера.

Посмотрим, как это реализуется:


&НаСервере
Процедура ОбработатьФайлНаСервере(АдресХранилища, ИмяФайла)
    
    // 1. Получаем двоичные данные из хранилища
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    Если ДвоичныеДанные = Неопределено Тогда
        Сообщить("Ошибка получения файла из хранилища!");
        Возврат;
    КонецЕсли;
    
    // 2. Создаем временный файл на диске СЕРВЕРА.
    // Серверу нужны права на запись в папку временных файлов (обычно они есть).
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
    
    // 3. Записываем двоичные данные на диск сервера
    ДвоичныеДанные.Записать(ИмяВременногоФайла);
    
    Попытка
        // Теперь мы можем работать с файлом по локальному пути сервера
        // Например, откроем его через ТабличныйДокумент
        ТабДок = Новый ТабличныйДокумент;
        ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
        
        // Здесь размещаем логику парсинга файла
        // ...
        
        Сообщить("Файл успешно прочитан на сервере. Строк: " + ТабДок.ВысотаТаблицы);
        
    Исключение
        Сообщить("Не удалось прочитать файл: " + ОписаниеОшибки());
    КонецПопытки;
    
    // 4. Хорошим тоном считается удаление временного файла после работы
    УдалитьФайлы(ИмяВременногоФайла);

КонецПроцедуры

Важные нюансы и типичные ошибки

Давайте проанализируем важные детали, которые часто упускают при реализации этого механизма:

  1. Привязка к форме (UUID):

    Обратите внимание на использование УникальныйИдентификатор при помещении файла. Если вы не укажете его, файл поместится во временное хранилище, но может быть удален системой сразу после окончания серверного вызова. Привязка к форме гарантирует, что файл "живет" пока открыта форма или пока мы явно его не удалим.

  2. Фоновые задания:

    Если длительная обработка файла занимает много времени, ее лучше выносить в фоновое задание. В этом случае адрес временного хранилища нужно передавать в параметры фонового задания. Важно помнить: фоновое задание не имеет доступа к контексту формы, поэтому данные должны быть переданы именно через хранилище.

  3. Расширение файла:

    При использовании ПолучитьИмяВременногоФайла желательно указывать правильное расширение (в примере мы указали "xlsx"). Некоторые методы чтения (например, ТабличныйДокумент.Прочитать) могут ориентироваться на расширение файла для определения формата.

  4. Безопасность:

    При использовании методов, работающих с внешними ресурсами (например, COM-объекты Excel на сервере), убедитесь, что настроены права доступа в 1С:Предприятие для пользователя, под которым запущена служба сервера 1С (обычно USR1CV8), включая доступ к временным папкам и права DCOM.

Альтернативный вариант: Объект без сохранения на диск

Иногда сохранять файл на диск сервера необязательно. Если вы работаете с текстовыми файлами или XML, объект ЧтениеТекста или ЧтениеXML может принимать поток данных напрямую.

Однако, если вы используете объект ДвоичныеДанные для передачи картинки или PDF, вы можете сразу сохранять их в реквизит справочника типа ХранилищеЗначения, минуя стадию записи во временный файл на диске:


// Пример сохранения в базу без промежуточного файла на диске
НовыйОбъект = Справочники.Файлы.СоздатьЭлемент();
НовыйОбъект.Имя = ИмяФайла;
// Создаем ХранилищеЗначения напрямую из ДвоичныхДанных
НовыйОбъект.ДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанные);
НовыйОбъект.Записать();

Заключение

Мы подробно рассмотрели, как преодолеть барьер между файловой системой клиента и сервера. Используя механизм временного хранилища, вы обеспечиваете надежную передачу данных независимо от того, где находится пользователь — в соседней комнате или работает через веб-браузер с другого конца света.

Основные выводы:

← На главную