При разработке на платформе 1С:Предприятие 8.3 (особенно на версиях ниже 8.3.15) программисты часто сталкиваются с проблемой асинхронности. Типичная ситуация: пользователь нажимает кнопку «Провести и закрыть», запускается процесс передачи файлов на сервер через метод НачатьПомещениеФайлов, но форма закрывается мгновенно, не дожидаясь окончания загрузки. В итоге файлы либо не попадают на сервер вовсе, либо временное хранилище очищается раньше, чем сервер успеет обработать данные.
В этой статье мы разберем, как технически правильно организовать процесс ожидания загрузки, чтобы гарантировать сохранение всех данных и при этом обеспечить комфортную работу пользователя. Мы проанализируем несколько подходов: от использования флагов состояния до реализации очереди загрузки в фоновом режиме.
Это классический и наиболее надежный способ управления асинхронными процессами в управляемых формах. Идея заключается в том, чтобы «перехватить» стандартное событие записи, отменить его, выполнить загрузку файлов и только после этого инициировать запись повторно.
Рассмотрим по шагам, как это реализовать:
ФайлыЗагружены с типом Булево (по умолчанию Ложь).ПередЗаписью проверим этот флаг. Если он не установлен, мы отменяем стандартную запись и запускаем процедуру загрузки.Истина и снова вызываем метод Записать().Проанализируем пример кода для реализации этой логики:
&НаКлиенте
Перем ВыполняетсяЗагрузка; // Локальная переменная для блокировки повторных нажатий
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Если ФайлыЗагружены Тогда
// Если файлы уже на сервере, разрешаем штатную запись
Возврат;
КонецЕсли;
// Если файлы еще не загружены, отменяем текущую запись
Отказ = Истина;
// Запускаем асинхронный процесс помещения файлов
ВыполнитьЗагрузкуФайловНаСервер(ПараметрыЗаписи);
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьЗагрузкуФайловНаСервер(ПараметрыЗаписи)
ОписаниеОповещения = Новый ОписаниеОповещения("ЗавершитьЗагрузкуФайлов", ЭтотОбъект, ПараметрыЗаписи);
// Формируем массив файлов для загрузки (ОписаниеПередаваемогоФайла)
// Для примера используем упрощенный вызов
НачатьПомещениеФайлов(ОписаниеОповещения, МассивФайлов, , Ложь, УникальныйИдентификатор);
Состояние("Пожалуйста, подождите. Идет передача фотографий на сервер...");
КонецПроцедуры
&НаКлиенте
Процедура ЗавершитьЗагрузкуФайлов(Результат, ДополнительныеПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат; // Пользователь отменил загрузку или произошла ошибка
КонецЕсли;
// Устанавливаем флаг, что данные на сервере (во временном хранилище)
ФайлыЗагружены = Истина;
// Обрабатываем полученные адреса во временном хранилище (если нужно)
// ... логика привязки адресов к объекту ...
// Повторно вызываем запись объекта с теми же параметрами
ЭтотОбъект.Записать(ДополнительныеПараметры);
КонецПроцедуры
Этот подход гарантирует, что документ не будет проведен, пока НачатьПомещениеФайлов не вернет управление в процедуру оповещения. Важно отметить, что использование УникальныйИдентификатор формы при помещении файлов критично: если форма закроется, данные во временном хранилище, привязанные к этому идентификатору, будут удалены.
Иногда проще полностью контролировать поведение интерфейса, заменив стандартную кнопку «Провести и закрыть» на собственную. Это позволяет избежать сложных манипуляций в ПередЗаписью и сделать логику более линейной для понимания.
Разберем алгоритм работы такой кнопки:
ЭтаФорма.Закрыть().Выясним причину, почему это удобно: вы можете заблокировать элементы формы на время загрузки, чтобы пользователь случайно не изменил другие реквизиты, пока файлы «едут» на сервер. Это повышает стабильность работы приложения.
Рассмотрим ситуацию, когда файлов много (например, 10-15 фотографий высокого разрешения) и ожидание их загрузки раздражает пользователя. В этом случае мы можем применить стратегию «отложенной обработки».
Для этого выполним следующие действия:
Важный нюанс: данный метод требует, чтобы сервер 1С имел прямой доступ к файловой системе клиента (что редкость в веб-клиенте) или чтобы файлы были предварительно помещены в общую сетевую папку (шару). Если работа идет через интернет, этот способ потребует использования внешней компоненты для фоновой передачи данных по протоколу HTTP или FTP.
Рассмотрим подробнее техническую деталь, упомянутую в обсуждениях. Часто разработчики пытаются использовать ПоместитьВоВременноеХранилище в цикле для каждого файла. Проанализируем, почему это плохо: каждый такой вызов создает отдельное соединение с сервером. Если у вас 10 фотографий, система сделает 10 последовательных запросов, что значительно медленнее одного пакетного запроса.
Проанализируем пример правильного формирования массива для пакетной загрузки:
&НаКлиенте
Процедура ПодготовитьИЗагрузитьФайлы()
МассивОписаний = Новый Массив;
Для Каждого СтрокаФото Из Объект.Фотографии Цикл
Если ЭтоАдресВременногоХранилища(СтрокаФото.Путь) Тогда
Продолжить; // Файл уже загружен
КонецЕсли;
Описание = Новый ОписаниеПередаваемогоФайла(СтрокаФото.ПолныйПутьНаДиске);
МассивОписаний.Добавить(Описание);
КонецЦикла;
Если МассивОписаний.Количество() > 0 Тогда
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеМассовойЗагрузки", ЭтотОбъект);
НачатьПомещениеФайлов(ОписаниеОповещения, МассивОписаний, , Ложь, УникальныйИдентификатор);
КонецЕсли;
КонецПроцедуры
Использование метода НачатьПомещениеФайлов (во множественном числе) позволяет платформе оптимизировать трафик и передать все данные максимально быстро, используя один поток передачи.
При реализации любого из перечисленных методов, следует помнить о следующих моментах:
Новый УникальныйИдентификатор для сессии.Обещаний и ключевое слово Ждать, что делает код значительно чище. Однако для версии 8.3.13 описанные выше методы через ОписаниеОповещения являются единственно верными.Подведем итог: наиболее сбалансированным решением для задачи ожидания загрузки является комбинация флага в ПередЗаписью и пакетной передачи файлов. Это гарантирует целостность данных и сохраняет стандартный пользовательский интерфейс 1С.