Как правильно передать файл с клиента на сервер в 1С 8.3 (Асинхронно)?

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

При разработке на платформе 1С:Предприятие 8.3 (поможет нейросеть для генерации кода 1С и метаданных), особенно в условиях веб-клиента или при использовании режима совместимости с отказом от модальности, часто возникает задача передачи файлов с компьютера пользователя (Клиента) на Сервер 1С. Это необходимо для загрузки картинок номенклатуры, массового импорта данных из Excel и других форматов, сохранения сканов документов и многого другого.

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

Понимаем архитектуру: Клиент и Сервер

Прежде чем мы перейдем к написанию кода, проанализируем, почему нельзя просто прочитать файл по пути C:\Users\Admin\Desktop\file.txt на сервере. В клиент-серверной архитектуре:

  1. Клиент — это приложение, запущенное на компьютере пользователя (Тонкий клиент, Веб-клиент). Он "видит" локальные диски пользователя.
  2. Сервер — это компьютер (или кластер), где работает сервер 1С. Он "видит" только свои локальные диски и доступные сетевые ресурсы, но не имеет прямого доступа к диску C: компьютера пользователя.

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

Механизм Временного хранилища

Посмотрим на временное хранилище как на "облачную папку" внутри сеанса пользователя, доступную и с клиента, и с сервера. Алгоритм действий всегда выглядит так:

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

Шаг 1: Асинхронный выбор и помещение файла

В современных конфигурациях (и особенно в веб-клиенте) запрещено использование синхронных (блокирующих) вызовов, таких как ДиалогВыбораФайла.Выбрать(). Вместо этого мы будем использовать асинхронные методы с описанием оповещения. Это означает, что мы говорим системе: "Начни выбор файла, а когда пользователь закончит — выполни вот эту процедуру".

Рассмотрим пример кода команды на форме. Создадим процедуру, которая инициирует процесс.


&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
    
    // Создаем описание оповещения. Это "инструкция", какую процедуру запустить после выбора.
    // "ПослеВыбораФайла" - имя экспортной процедуры в этом же модуле.
    // ЭтотОбъект - где искать процедуру.
    Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
    
    // Настраиваем параметры диалога
    Проводник = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    Проводник.Заголовок = "Выберите файл для загрузки";
    Проводник.МножественныйВыбор = Ложь;
    
    // Начинаем помещение файла. Этот метод сам откроет диалог выбора.
    // Параметры: Оповещение, УникальныйИдентификатор формы (для привязки хранилища),
    // ИмяФайла (пустое, так как выбираем), Адрес (пустой, получим новый).
    НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);

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

Обратим внимание на метод НачатьПомещениеФайла. Он объединяет в себе две операции: показ диалога выбора файла пользователю и автоматическую загрузку выбранного файла во временное хранилище. Это наиболее оптимальный способ.

Четвертый параметр Истина означает "Интерактивно", то есть пользователю будет показано окно выбора. Если бы путь к файлу был нам известен заранее, мы могли бы передать Ложь.

Шаг 2: Обработка результата выбора (на Клиенте)

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


&НаКлиенте
Процедура ПослеВыбораФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
    
    // Проверяем, был ли выбран файл или пользователь нажал "Отмена"
    Если Результат = Ложь Тогда
        Возврат;
    КонецЕсли;
    
    // В переменной "Адрес" теперь содержится ссылка на файл во временном хранилище.
    // ВыбранноеИмяФайла - это локальный путь (например, C:\Docs\File.xml), он нужен только для информации (например, расширение узнать).
    
    // Теперь вызываем серверную процедуру и передаем туда адрес
    ОбработатьФайлНаСервере(Адрес, ВыбранноеИмяФайла);

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

Важный момент: Переменная Адрес содержит путь к данным, которые УЖЕ находятся в памяти сервера (во временном хранилище). Нам не нужно передавать сами двоичные данные, достаточно передать эту строку.

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

Переходим к серверной части. Здесь мы должны забрать данные из временного хранилища. Рассмотрим, как это сделать правильно.


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

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

Вспомогательные функции

В коде выше мы использовали функцию ЗаглушкаРасширения, чтобы сохранить файл во временный каталог с тем же расширением, что и исходный. Это важно, если вам требуется, например, автоматическая конвертация файлов из CSV в XLS, так как табличный документ может не открыться корректно без нужного расширения. Разберем, как получить расширение из имени файла.


&НаСервереБезКонтекста
Функция ЗаглушкаРасширения(ПолноеИмяФайла)
    
    // Находим последнюю точку
    ПозицияТочки = СтрНайти(ПолноеИмяФайла, ".", НаправлениеПоиска.СКонца);
    
    Если ПозицияТочки > 0 Тогда
        Возврат Сред(ПолноеИмяФайла, ПозицияТочки + 1); // Возвращаем "xml", "xlsx" и т.д.
    Иначе
        Возврат "tmp"; // Если расширения нет
    КонецЕсли;

КонецФункции

Нюансы работы с Временным хранилищем

При работе с передачей файлов важно учитывать жизненный цикл временного хранилища. При вызове НачатьПомещениеФайла мы передавали УникальныйИдентификатор формы.

В нашем примере мы сразу передаем адрес на сервер и обрабатываем его, поэтому использование идентификатора формы является безопасным и рекомендуемым вариантом.

Оптимизация и работа с большими файлами

Если вам необходимо передавать очень большие файлы (сотни мегабайт), стандартный метод может вызвать перерасход оперативной памяти рабочего процесса сервера, так как ПолучитьИзВременногоХранилища загружает весь файл в RAM.

В таких случаях рекомендуется:

  1. Использовать методы разбиения файла на части (чанками) на клиенте и последовательной отправки.
  2. Использовать объект ФайловыйПоток (FileStream), доступный в новых версиях платформы, для потокового чтения без полной загрузки в память.

Однако для большинства задач по обмену данными, включая O-Bridges: простой обмен данными между базами 1С и приложениями, описанный выше метод через НачатьПомещениеФайла и ПолучитьИзВременногоХранилища является стандартом де-факто.

Резюме по решению проблемы

Подведем итог. Чтобы передать файл с клиента на сервер и избежать ошибок доступа и модальности, выполним следующие действия:

  1. Используем асинхронный метод НачатьПомещениеФайла на клиенте.
  2. Используем ОписаниеОповещения для обработки результата выбора.
  3. Передаем полученный строковый адрес во временном хранилище на сервер.
  4. На сервере получаем данные методом ПолучитьИзВременногоХранилища.
  5. При необходимости сохраняем во временный файл на сервере для чтения специфическими методами (Excel, XML, DBF) — для этого подойдёт универсальная загрузка данных из Excel в 1С.

Такой подход гарантирует работоспособность вашего кода в веб-клиенте, в облачных сервисах (1С:Фреш) и на мобильной платформе 1С.

← На главную