Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда код, безупречно работающий в тонком или толстом клиенте, внезапно отказывается функционировать в веб-клиенте. Одной из наиболее распространенных проблем является возникновение ошибки «Использование модальных окон в данном режиме запрещено!» при попытке загрузить или обработать локальный файл пользователя.
В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем скрытые баги в логике работы с файлами, выясним причины игнорирования платформой настроек модальности конфигурации и рассмотрим современные стандарты разработки асинхронных процедур для платформы 1С:Предприятие.
Для того чтобы понять природу проблемы, нам необходимо обратиться к архитектуре современных веб-браузеров. В отличие от настольного приложения (тонкого клиента), веб-клиент 1С выполняется внутри браузера в изолированной среде — так называемой «песочнице». Это накладывает жесткие ограничения на безопасность:
showModalDialog), поскольку они полностью замораживают интерфейс браузера, ухудшая пользовательский опыт и создавая угрозы безопасности.Начиная с версии платформы 8.3.10, в веб-клиенте 1С полностью прекращена поддержка модальных окон, независимо от того, какое значение установлено в свойствах конфигурации для параметра Режим использования модальности. Даже если там указано «Разрешить», веб-клиент проигнорирует эту настройку и выбросит исключение при первой же попытке открыть модальный диалог или выполнить блокирующую синхронную операцию.
Давайте подробно проанализируем фрагмент кода, который вызвал затруднения у автора темы на форуме. Проблема скрывается внутри процедуры-обработчика оповещения:
&НаКлиенте
Процедура ОбработатьВыборФайла(ПомещенныеФайлы, ДополнительныеПараметры) Экспорт
Если ПомещенныеФайлы = Неопределено Тогда
Возврат;
КонецЕсли;
Для каждого ПереданныйФайл Из ПомещенныеФайлы Цикл
ПутьКФайлу = ПереданныйФайл.Имя;
Адрес = ПереданныйФайл.Хранение;
Объект.ФайлНаЛокальномПК=ПереданныйФайл.Имя;
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу); // <-- ИСТОЧНИК КРИТИЧЕСКОЙ ОШИБКИ
ИмяФайла = ПоместитьВоВременноеХранилище(ДвоичныеДанные );
КонецЦикла;
СоздатьСсылкуНаСервере(ИмяФайла);
КонецПроцедуры
В этом коде содержатся сразу три серьезные ошибки, которые делают его неработоспособным в веб-клиенте:
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу) выполняется на стороне клиента. Конструктор Новый ДвоичныеДанные пытается выполнить синхронное чтение файла с локального диска компьютера. В веб-клиенте это немедленно вызывает блокировку со стороны безопасности браузера, что и приводит к ошибке запрета модальных/синхронных вызовов.Имя объекта ОписаниеПереданногоФайла (или ПереданныйФайл) в веб-клиенте содержит только краткое имя файла (например, report.xlsx), а не полный путь на диске (вроде C:\Users\Username\Documents\report.xlsx). Таким образом, даже в тонком клиенте попытка вызвать Новый ДвоичныеДанные("report.xlsx") без указания полного пути приведет к ошибке «Файл не найден».НачатьПомещениеФайлов спроектирован так, что он уже самостоятельно загружает выбранные пользователем файлы во временное хранилище на сервере 1С. Готовый уникальный адрес этого хранилища возвращается в свойстве ПереданныйФайл.Хранение. Повторно читать файл с локального диска, создавать двоичные данные и снова помещать их в хранилище через метод ПоместитьВоВременноеХранилище нет абсолютно никакой необходимости!Для того чтобы исправить ошибку, нам нужно полностью убрать повторное чтение файла на клиенте. Мы должны сразу использовать готовый адрес временного хранилища, который платформа нам предоставила. Изменим процедуру-обработчик следующим образом:
&НаКлиенте
Процедура ОбработатьВыборФайла(ПомещенныеФайлы, ДополнительныеПараметры) Экспорт
Если ПомещенныеФайлы = Неопределено Тогда
Возврат;
КонецЕсли;
Для каждого ПереданныйФайл Из ПомещенныеФайлы Цикл
// Записываем имя файла для отображения в интерфейсе
Объект.ФайлНаЛокальномПК = ПереданныйФайл.Имя;
// Передаем на сервер готовый адрес во временном хранилище
СоздатьСсылкуНаСервере(ПереданныйФайл.Хранение);
КонецЦикла;
КонецПроцедуры
Посмотрим, насколько упростился код! Теперь клиентская процедура работает молниеносно, не совершает запрещенных синхронных обращений к диску и не вызывает никаких модальных окон в веб-клиенте.
На стороне сервера (в процедуре СоздатьСсылкуНаСервере) мы можем спокойно работать с полученным адресом временного хранилища:
&НаСервере
Процедура СоздатьСсылкуНаСервере(АдресВХ)
// Здесь мы можем получить двоичные данные файла на стороне сервера абсолютно безопасно
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресВХ);
// Выполняем необходимую бизнес-логику (сохранение в базу данных, обработка и т.д.)
// ...
КонецПроцедуры
Помимо настройки модальности, в свойствах конфигурации существует крайне важный параметр — Режим использования синхронных вызовов расширений платформы и внешних компонент. В современных конфигурациях 1С (особенно построенных на базе БСП последних версий) этот параметр по умолчанию установлен в значение «Не использовать».
При таком режиме работы любые синхронные методы работы с файловой системой (например, использование объекта Файл, проверка существования файлов через синхронные методы и т.д.) будут приводить к аварийному завершению работы в веб-клиенте. Платформа требует строгого разделения кода на интерактивный асинхронный (на клиенте) и исполняемый (на сервере).
В процессе обсуждения на форуме участники затронули тему использования расширения работы с файлами в веб-клиенте. Давайте проясним эту ситуацию:
Развитие платформы 1С привело к тому, что старые методы НачатьПомещениеФайла() и НачатьПомещениеФайлов() были признаны устаревшими и не рекомендуются к использованию. Вместо них разработчикам следует использовать современные асинхронные аналоги, которые обеспечивают максимальную совместимость с мобильным клиентом и веб-клиентом под любыми операционными системами.
Давайте рассмотрим пример написания современного, кодобезопасного механизма для выбора и помещения файлов на сервер с использованием метода НачатьПомещениеФайловНаСервер():
&НаКлиенте
Процедура НачатьЗагрузкуФайла()
// Создаем описание оповещения, указывая процедуру завершения
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПомещенияФайлов", ЭтаФорма);
// Настраиваем параметры диалога выбора файлов
ПараметрыДиалога = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ПараметрыДиалога.Заголовок = "Выберите файл для загрузки в систему";
ПараметрыДиалога.Фильтр = "Документы PDF (*.pdf)|*.pdf|Все файлы (*.*)|*.*";
// Используем современный асинхронный метод платформы
НачатьПомещениеФайловНаСервер(ОписаниеОповещения, , , ПараметрыДиалога, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайлов(РезультатПомещения, ДополнительныеПараметры) Экспорт
// Проверяем, выбрал ли пользователь файл или закрыл диалог
Если РезультатПомещения = Неопределено Или РезультатПомещения.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Обрабатываем результат
Для каждого ФайлИзРезультата Из РезультатПомещения Цикл
// Получаем информацию о загруженном файле
ИмяФайла = ФайлИзРезультата.Имя;
АдресВХ = ФайлИзРезультата.Адрес; // Ссылка на временное хранилище
// Передаем адрес на сервер для дальнейшей обработки
ОбработатьФайлНаСервере(ИмяФайла, АдресВХ);
КонецЦикла;
КонецПроцедуры
Использование метода НачатьПомещениеФайловНаСервер() имеет ряд неоспоримых преимуществ:
Таким образом, следуя современным стандартам разработки в 1С и полностью отказавшись от синхронных вызовов на стороне клиента, вы навсегда избавите свои решения от ошибок модальности в веб-клиенте и обеспечите их стабильную работу в любых браузерах.