При разработке на платформе 1С:Предприятие 8.3 (поможет нейросеть для генерации кода 1С и метаданных), особенно в условиях веб-клиента или при использовании режима совместимости с отказом от модальности, часто возникает задача передачи файлов с компьютера пользователя (Клиента) на Сервер 1С. Это необходимо для загрузки картинок номенклатуры, массового импорта данных из Excel и других форматов, сохранения сканов документов и многого другого.
Рассмотрим подробно, почему прямой доступ к файловой системе невозможен, разберем механизм Временного хранилища и напишем правильный, современный код для асинхронной передачи файлов, который будет работать во всех режимах запуска.
Прежде чем мы перейдем к написанию кода, проанализируем, почему нельзя просто прочитать файл по пути C:\Users\Admin\Desktop\file.txt на сервере. В клиент-серверной архитектуре:
Следовательно, чтобы обработать файл, мы должны физически "переместить" его данные с Клиента на Сервер. В платформе 1С для этого используется специальный механизм — Временное хранилище. В качестве готового интерактивного решения для перемещения файлов можно использовать двухпанельный файловый менеджер для обмена файлами между клиентом и сервером.
Посмотрим на временное хранилище как на "облачную папку" внутри сеанса пользователя, доступную и с клиента, и с сервера. Алгоритм действий всегда выглядит так:
e1cib/tempstorage/sd54...).В современных конфигурациях (и особенно в веб-клиенте) запрещено использование синхронных (блокирующих) вызовов, таких как ДиалогВыбораФайла.Выбрать(). Вместо этого мы будем использовать асинхронные методы с описанием оповещения. Это означает, что мы говорим системе: "Начни выбор файла, а когда пользователь закончит — выполни вот эту процедуру".
Рассмотрим пример кода команды на форме. Создадим процедуру, которая инициирует процесс.
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
// Создаем описание оповещения. Это "инструкция", какую процедуру запустить после выбора.
// "ПослеВыбораФайла" - имя экспортной процедуры в этом же модуле.
// ЭтотОбъект - где искать процедуру.
Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
// Настраиваем параметры диалога
Проводник = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Проводник.Заголовок = "Выберите файл для загрузки";
Проводник.МножественныйВыбор = Ложь;
// Начинаем помещение файла. Этот метод сам откроет диалог выбора.
// Параметры: Оповещение, УникальныйИдентификатор формы (для привязки хранилища),
// ИмяФайла (пустое, так как выбираем), Адрес (пустой, получим новый).
НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
Обратим внимание на метод НачатьПомещениеФайла. Он объединяет в себе две операции: показ диалога выбора файла пользователю и автоматическую загрузку выбранного файла во временное хранилище. Это наиболее оптимальный способ.
Четвертый параметр Истина означает "Интерактивно", то есть пользователю будет показано окно выбора. Если бы путь к файлу был нам известен заранее, мы могли бы передать Ложь.
Теперь создадим процедуру, которую мы указали в ОписаниеОповещения. Она получит управление, когда файл будет выбран и загружен (или если пользователь нажмет "Отмена").
&НаКлиенте
Процедура ПослеВыбораФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
// Проверяем, был ли выбран файл или пользователь нажал "Отмена"
Если Результат = Ложь Тогда
Возврат;
КонецЕсли;
// В переменной "Адрес" теперь содержится ссылка на файл во временном хранилище.
// ВыбранноеИмяФайла - это локальный путь (например, C:\Docs\File.xml), он нужен только для информации (например, расширение узнать).
// Теперь вызываем серверную процедуру и передаем туда адрес
ОбработатьФайлНаСервере(Адрес, ВыбранноеИмяФайла);
КонецПроцедуры
Важный момент: Переменная Адрес содержит путь к данным, которые УЖЕ находятся в памяти сервера (во временном хранилище). Нам не нужно передавать сами двоичные данные, достаточно передать эту строку.
Переходим к серверной части. Здесь мы должны забрать данные из временного хранилища. Рассмотрим, как это сделать правильно.
&НаСервере
Процедура ОбработатьФайлНаСервере(АдресВХранилище, ИмяФайла)
// Проверяем, что адрес не пустой (на всякий случай)
Если ЭтоАдресВременногоХранилища(АдресВХранилище) = Ложь Тогда
Сообщить("Ошибка передачи файла: некорректный адрес.");
Возврат;
КонецЕсли;
Попытка
// Получаем ДвоичныеДанные из хранилища
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресВХранилище);
// Теперь у нас есть объект ДвоичныеДанные.
// Мы можем:
// 1. Сохранить его в справочник.
// 2. Сохранить во временный файл на диске сервера для парсинга.
// Вариант 1: Сохранение в справочник "Файлы" (пример)
// НоваяКартинка = Справочники.Файлы.СоздатьЭлемент();
// НоваяКартинка.Наименование = ИмяФайла;
// НоваяКартинка.Данные = Новый ХранилищеЗначения(ДанныеФайла);
// НоваяКартинка.Записать();
// Вариант 2: Сохранение на диск сервера для чтения (например, это XML или Excel)
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(ЗаглушкаРасширения(ИмяФайла));
ДанныеФайла.Записать(ИмяВременногоФайла);
// Теперь файл физически лежит на диске сервера по пути ИмяВременногоФайла
// Можно его читать.
ТекстДок = Новый ТекстовыйДокумент;
ТекстДок.Прочитать(ИмяВременногоФайла);
// ... тут логика обработки ...
// Не забываем удалить временный файл после использования, чтобы не засорять диск сервера
УдалитьФайлы(ИмяВременногоФайла);
Сообщить("Файл успешно обработан!");
Исключение
Сообщить("Произошла ошибка при обработке файла: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
В коде выше мы использовали функцию ЗаглушкаРасширения, чтобы сохранить файл во временный каталог с тем же расширением, что и исходный. Это важно, если вам требуется, например, автоматическая конвертация файлов из CSV в XLS, так как табличный документ может не открыться корректно без нужного расширения. Разберем, как получить расширение из имени файла.
&НаСервереБезКонтекста
Функция ЗаглушкаРасширения(ПолноеИмяФайла)
// Находим последнюю точку
ПозицияТочки = СтрНайти(ПолноеИмяФайла, ".", НаправлениеПоиска.СКонца);
Если ПозицияТочки > 0 Тогда
Возврат Сред(ПолноеИмяФайла, ПозицияТочки + 1); // Возвращаем "xml", "xlsx" и т.д.
Иначе
Возврат "tmp"; // Если расширения нет
КонецЕсли;
КонецФункции
При работе с передачей файлов важно учитывать жизненный цикл временного хранилища. При вызове НачатьПомещениеФайла мы передавали УникальныйИдентификатор формы.
В нашем примере мы сразу передаем адрес на сервер и обрабатываем его, поэтому использование идентификатора формы является безопасным и рекомендуемым вариантом.
Если вам необходимо передавать очень большие файлы (сотни мегабайт), стандартный метод может вызвать перерасход оперативной памяти рабочего процесса сервера, так как ПолучитьИзВременногоХранилища загружает весь файл в RAM.
В таких случаях рекомендуется:
ФайловыйПоток (FileStream), доступный в новых версиях платформы, для потокового чтения без полной загрузки в память.Однако для большинства задач по обмену данными, включая O-Bridges: простой обмен данными между базами 1С и приложениями, описанный выше метод через НачатьПомещениеФайла и ПолучитьИзВременногоХранилища является стандартом де-факто.
Подведем итог. Чтобы передать файл с клиента на сервер и избежать ошибок доступа и модальности, выполним следующие действия:
НачатьПомещениеФайла на клиенте.ОписаниеОповещения для обработки результата выбора.ПолучитьИзВременногоХранилища.Такой подход гарантирует работоспособность вашего кода в веб-клиенте, в облачных сервисах (1С:Фреш) и на мобильной платформе 1С.