При разработке под управляемый интерфейс 1С:Предприятия программисты часто сталкиваются с ситуацией, когда код, написанный и протестированный в тонком клиенте, отказывается работать в веб-клиенте. Одной из классических проблем является использование устаревшего метода НачатьПомещениеФайла во внешних обработках. В тонком клиенте выбор файла и его загрузка проходят штатно, а в веб-клиенте (в браузере) после выбора файла в интерактивном диалоге ничего не происходит — обработчик оповещения о завершении операции просто не вызывается.
В этой статье мы подробно разберем, почему возникает такое поведение, заглянем во внутренние механизмы управления памятью веб-браузеров и рассмотрим несколько надежных способов решения этой проблемы, включая использование современных асинхронных методов платформы и возможностей Библиотеки стандартных подсистем (БСП).
Основное различие между тонким и веб-клиентом в данном сценарии кроется в механизме управления памятью (сборщике мусора — Garbage Collector) и особенностях работы асинхронного кода в браузере.
Давайте проанализируем, как выполняется асинхронный вызов команды во внешней обработке (например, при заполнении объекта документа без открытия формы):
НачатьПомещениеФайла, в который передается ОписаниеОповещения. Это описание содержит ссылку на текущий контекст выполнения (параметр ЭтотОбъект).Если же форма обработки открыта интерактивно, она регистрируется в интерфейсе веб-клиента. Платформа принудительно удерживает контекст формы и её модуля в памяти до тех пор, пока пользователь её не закроет. Именно поэтому при открытой форме обработчик оповещения всегда успешно отрабатывает — его контекст физически существует в памяти на момент выбора файла.
Рассмотрим пример исходного проблемного кода, в котором веб-клиент теряет связь с обработчиком оповещения:
#Если ВебКлиент Тогда
ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогВыбораФайла.Заголовок = "Выбор файла для загрузки";
// В веб-клиенте этот вызов завершит процедуру, контекст ЭтотОбъект очистится,
// и процедура ОкончаниеВыбораФайла никогда не вызовется.
НачатьПомещениеФайла(
Новый ОписаниеОповещения("ОкончаниеВыбораФайла", ЭтотОбъект),
,
ДиалогВыбораФайла,
,
УникальныйИдентификатор
);
#КонецЕсли
---
Самый быстрый, хотя и не самый элегантный способ обойти ограничение сборщика мусора в веб-клиенте — это принудительное открытие формы внешней обработки. Открытая форма выступает в роли «якоря» для контекста выполнения.
Для реализации этого сценария настройте логику работы команды следующим образом:
ПриОткрытии или через обработчик ожидания).Закрыть().Посмотрим на пример кода, реализующий данный обходной путь:
&НаКлиенте
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначения) Экспорт
#Если ВебКлиент Тогда
// Для веб-клиента открываем форму, чтобы удержать контекст в памяти
ПараметрыФормы = Новый Структура("ОбъектыНазначения", ОбъектыНазначения);
ОткрытьФорму("ВнешняяОбработка.ЗагрузкаДанных.Форма", ПараметрыФормы);
#Иначе
// В тонком клиенте можем работать напрямую без открытия формы
ЗапуститьПомещениеФайлаБезФормы(ОбъектыНазначения);
#КонецЕсли
КонецПроцедуры
---
Метод НачатьПомещениеФайла признан устаревшим, начиная с версии платформы 8.3.15. В современных версиях 1С:Предприятия для работы с файлами были введены более надежные и оптимизированные асинхронные методы: НачатьПомещениеФайлаНаСервер() и НачатьПомещениеФайловНаСервер().
Эти новые методы спроектированы с учетом полной асинхронности веб-клиента. Они используют специальные объекты параметров и умеют корректно сохранять состояние сессии без жесткой привязки к контексту временных модулей команд. Кроме того, они позволяют загружать файлы в браузерах без предварительной установки расширения работы с файлами.
Давайте разберем пример использования нового метода НачатьПомещениеФайлаНаСервер:
&НаКлиенте
Процедура НачатьЗагрузкуФайла()
// Создаем описание оповещения, указывая процедуру завершения.
// Если контекст ЭтотОбъект может быть уничтожен, в качестве модуля
// обработчика лучше использовать форму-владелец или общий модуль.
Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
// Настраиваем параметры диалога
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
ПараметрыДиалога.Фильтр = "Файлы Excel (*.xlsx)|*.xlsx|Все файлы (*.*)|*.*";
ПараметрыДиалога.Заголовок = "Выберите файл для загрузки данных";
// Запускаем асинпомещение файла на сервер
НачатьПомещениеФайлаНаСервер(Оповещение, , , ПараметрыДиалога, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(РезультатПомещения, ДополнительныеПараметры) Экспорт
Если РезультатПомещения = Неопределено Тогда
Возврат; // Пользователь отказался от выбора файла
КонецЕсли;
Если РезультатПомещения.ИмяФайла = "" Тогда
ВызватьИсключение "Файл не был выбран или не загрузился.";
КонецЕсли;
// Получаем адрес во временном хранилище
АдресХранилища = РезультатПомещения.Адрес;
ПоказатьПредупреждение(, "Файл успешно загружен во временное хранилище!");
// Дальнейшая обработка файла на сервере...
КонецПроцедуры
---
Если ваша конфигурация разработана на базе БСП, настоятельно рекомендуется отказаться от прямых вызовов низкоуровневых методов платформы в пользу стандартных общих модулей подсистемы «Работа с файлами».
Использование методов БСП полностью скрывает от разработчика особенности работы веб-клиента и тонкого клиента, самостоятельно управляя контекстом асинхронного вызова и сессией браузера. Вам больше не придется писать ветвление кода #Если ВебКлиент Тогда.
Рассмотрим, как выглядит загрузка файла через БСП:
&НаКлиенте
Процедура ЗагрузитьФайлЧерезБСП()
ПараметрыЗагрузки = ФайловаяСистемаКлиент.ПараметрыЗагрузкиФайла();
ПараметрыЗагрузки.Фильтр = "Текстовые файлы (*.txt)|*.txt";
ПараметрыЗагрузки.ИдентификаторФормы = УникальныйИдентификатор;
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗагрузкиФайлаБСП", ЭтотОбъект);
// БСП сама позаботится о корректном вызове в веб-клиенте
ФайловаяСистемаКлиент.ЗагрузитьФайл(ОписаниеОповещения, ПараметрыЗагрузки);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗагрузкиФайлаБСП(Результат, ДополнительныеПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат; // Выбор отменен
КонецЕсли;
// Результат содержит структуру с адресом во временном хранилище и полным именем файла
АдресФайла = Результат.Адрес;
ИмяФайла = Результат.Имя;
// Обрабатываем загруженный файл
КонецПроцедуры
---
Если вы разрабатываете команду заполнения объекта для конкретного документа или справочника, у вас под рукой всегда есть контекст формы самого заполняемого объекта (параметр ВладелецФормы или форма, из которой была запущена внешняя обработка).
Вместо того чтобы передавать временный ЭтотОбъект (модуль внешней обработки) в ОписаниеОповещения, вы можете передать туда контекст формы открытого документа. Сама форма документа гарантированно открыта на экране и удерживается платформой в памяти браузера, поэтому ее методы не будут очищены сборщиком мусора во время выбора файла пользователем.
Для этого:
ОписаниеОповещения указывайте форму-владельца в качестве модуля, содержащего экспортную процедуру обработки завершения.---
При тестировании работы с файлами в веб-клиенте под управлением различных браузеров (Google Chrome, Mozilla Firefox, Яндекс.Браузер) разработчики иногда сталкиваются со специфической ошибкой платформы: при использовании сложных фильтров файлов (например, ограничение строго по расширению .xlsx или .dbf) в интерактивном диалоге выбора файлов кнопка подтверждения («Открыть»/«ОК») может стать неактивной или заблокированной после выбора файла.
Если вы столкнулись с тем, что диалоговое окно открывается, но файл невозможно отправить на сервер, выполните следующие действия:
Все файлы (*.*)|*.*).Применяя данные рекомендации и переходя на современные методы платформы, вы обеспечите стабильную и быструю работу механизмов загрузки файлов в любых веб-клиентах и браузерах!