При переходе от толстого или тонкого клиента к веб-клиенту многие разработчики сталкиваются с тем, что привычные методы работы с файловой системой перестают работать. Браузер — это изолированная среда ("песочница"), которая ради безопасности не позволяет сайтам (а веб-клиент для браузера — это сайт) свободно читать и писать файлы на диск пользователя C:\ или D:\.
В этой статье мы подробно разберем, как адаптировать механизмы сохранения отчетов, прикрепления файлов и их отправки через HTTP для работы в веб-клиенте 1С. Мы рассмотрим архитектурно верные решения, которые избавят вас от лишних ошибок.
В классическом тонком клиенте (Desktop) мы привыкли использовать следующий алгоритм:
ПолучитьИмяВременногоФайла().Записать().В веб-клиенте этот код вызовет ошибку или ничего не сделает, так как у браузера нет прямого доступа к временным папкам операционной системы таким образом. Давайте разберем правильные подходы к трем основным задачам.
Частая задача: пользователь формирует отчет, нажимает кнопку, и система должна сохранить этот отчет в формате Excel (XLSX) и прикрепить его к справочнику "Файлы" или к документу.
Ошибка проектирования: Пытаться сохранить файл на клиенте, чтобы потом тут же загрузить его обратно на сервер. Это лишний трафик и лишние сложности с правами доступа браузера.
Правильное решение: Все действия по генерации и сохранению файла нужно выполнять на стороне сервера. Клиент (браузер) должен лишь отправить команду.
Разберем алгоритм:
Посмотрим на пример кода, как это реализовать, используя потоки в памяти (это быстрее и надежнее, чем создавать мусорные файлы на диске сервера):
// Процедура на Сервере
Процедура ПрикрепитьОтчетКОбъектуНаСервере(СсылкаНаОбъект)
// 1. Формируем отчет
ТабДок = Новый ТабличныйДокумент;
// ... здесь код заполнения макета и параметров ...
// 2. Сохраняем в поток в памяти (без создания файла на диске)
ПотокВПамяти = Новый ПотокВПамяти();
ТабДок.Записать(ПотокВПамяти, ТипФайлаТабличногоДокумента.XLSX);
// 3. Получаем двоичные данные из потока
ДвоичныеДанныеФайла = ПотокВПамяти.ЗакрытьИПолучитьДвоичныеДанные();
// 4. Помещаем во временное хранилище (если нужно для дальнейшей обработки)
// или сразу создаем присоединенный файл
АдресВХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла);
// Пример создания файла (зависит от вашей конфигурации/БСП)
НовыйФайл = Справочники.Файлы.СоздатьЭлемент();
НовыйФайл.ВладелецФайла = СсылкаНаОбъект;
НовыйФайл.Наименование = "Отчет от " + ТекущаяДата() + ".xlsx";
// ... логика записи данных в регистры или реквизиты хранения файлов ...
// РаботаСУниверсальнымиМеханизмами.ДобавитьФайл(... АдресВХранилище ...);
КонецПроцедуры
Таким образом, мы полностью исключаем клиентскую машину из процесса генерации файла. Это работает быстро и стабильно в любом клиенте, включая веб.
Иногда возникают ситуации, когда файл нужно отправить во внешнюю систему, но сервер 1С находится за строгим фаерволом, который блокирует исходящие соединения. В этом случае единственным "окном" в интернет является браузер пользователя.
Чтобы отправить файл с клиента (из браузера), нам нужно сначала передать данные этого файла с сервера на клиент.
Алгоритм действий:
ДвоичныеДанные.ПоместитьВоВременноеХранилище. Получаем навигационную ссылку (Адрес).Однако здесь нас подстерегает серьезная проблема веб-клиента — CORS (Cross-Origin Resource Sharing).
Важное предупреждение про CORS:
Если вы попытаетесь из веб-клиента (который загружен, например, с домена my-1c-server.ru) отправить AJAX/HTTP запрос на чужой домен (например, api.external-service.com), браузер заблокирует этот запрос, если внешний сервер специально не разрешает такие запросы заголовками Access-Control-Allow-Origin.
Если внешний сервис вам не подконтролен, отправить запрос с веб-клиента, скорее всего, не получится. В таком случае вам придется решать вопрос с администрированием сети и разрешать доступ серверу 1С.
Если же CORS настроен или вы работаете в рамках одного домена, код на клиенте будет выглядеть так:
// Асинхронная процедура на Клиенте
Асинх Процедура ОтправитьФайлСКлиента(АдресФайлаВХранилище)
// Получаем двоичные данные из временного хранилища на клиент
// В веб-клиенте это может потребовать дополнительных шагов,
// но для HTTP соединения часто достаточно передать ДвоичныеДанные
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресФайлаВХранилище); // Внимание: работает не во всех контекстах веб-клиента напрямую с большими объемами
// Создаем соединение
Соединение = Новый HTTPСоединение("api.example.com", 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос("/upload");
Запрос.УстановитьТелоИзДвоичныхДанных(ДанныеФайла);
// Асинхронный вызов, чтобы не блокировать интерфейс браузера
Попытка
Ответ = Ждать Соединение.ВызватьHTTPМетодАсинх("POST", Запрос);
Сообщить("Файл отправлен, код ответа: " + Ответ.КодСостояния);
Исключение
Сообщить("Ошибка отправки: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Как в веб-клиенте показать пользователю сформированный Excel или PDF? Привычный ЗапуститьПриложение() здесь работает иначе.
Есть два сценария:
Если у пользователя не установлено расширение работы с файлами, браузер не может просто так открыть Excel. Он может только скачать файл. Для этого используется метод ПолучитьФайл() или переход по навигационной ссылке.
Если расширение установлено, браузер получает больше прав и может инициировать запуск локального приложения. Для этого следует использовать современные асинхронные методы.
Используйте ЗапуститьПриложениеАсинх (доступно с версии 8.3.18 как стандарт, ранее через Callback).
&НаКлиенте
Асинх Процедура ОткрытьФайлПоКнопке(Команда)
// Предварительно файл должен быть помещен во временное хранилище на сервере
Адрес = ПолучитьАдресФайлаНаСервере();
ИмяФайла = "Отчет.xlsx";
Попытка
// Получаем файл из хранилища и предлагаем открыть
// Если подключено расширение - 1С попытается открыть его в Excel
// Если нет - браузер предложит сохранить файл
Ждать ПолучитьФайлССервераАсинх(Адрес, ИмяФайла, Истина);
Исключение
Сообщить("Не удалось получить файл: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Если вам нужно просто "открыть" файл, который уже лежит где-то локально (что редкость для веб-клиента) или по URL, используйте:
&НаКлиенте
Процедура ОткрытьВнешнююСсылку()
// Это заставит браузер открыть ссылку в новой вкладке или скачать файл
ЗапуститьПриложениеАсинх("https://mysite.com/report.pdf");
КонецПроцедуры
Если ваша конфигурация построена на базе Библиотеки Стандартных Подсистем (БСП), настоятельно рекомендуем не писать велосипеды для рассылки отчетов.
В БСП есть подсистема "Рассылка отчетов". Она позволяет:
Это полностью решает проблему "Как отправить файл", перенося всю работу на сервер в фоновый режим, что является самым надежным способом автоматизации.
ВременноеХранилище.Ждать, Асинх) вместо устаревших обработчиков оповещения.