При разработке систем документооборота и подсистем хранения файлов в 1С часто возникает задача организации доступа к прикрепленным документам. Иногда бизнес-требования или службы безопасности выдвигают жесткое условие: открывать двоичные данные из хранилища значений во внешних приложениях (MS Word, MS Excel, PDF-просмотрщики) строго без сохранения временных файлов на локальный диск пользователя. Вторая сопутствующая проблема — некорректная работа внутренних гиперссылок (якорей с символом #) при выводе HTML-документов непосредственно из строки текста.
Разберем подробнее, почему прямое открытие файлов во внешних приложениях из оперативной памяти технически невозможно, как устроена безопасность веб-клиента и какими альтернативными способами можно решить эти задачи в платформах 1С.
Проанализируем техническую сторону взаимодействия платформы 1С, операционной системы и сторонних программ. Попытка передать двоичные данные из ХранилищеЗначений напрямую во внешнее приложение упирается в архитектурные ограничения систем:
C:\Users\...\AppData\Local\Temp\file.docx). Они не умеют считывать произвольные блоки байтов напрямую из оперативной памяти процесса 1С.Таким образом, штатный и единственный рабочий сценарий для открытия файла сторонним приложением — это запись двоичных данных во временный каталог клиента с помощью функции ПолучитьИмяВременногоФайла(), запуск приложения и последующее удаление файла при завершении работы.
Если бизнес-требование запрещает создавать локальные файлы на диске, рассмотрим способы, которые позволяют отобразить содержимое данных средствами самой платформы 1С или браузера:
Для документов формата PDF, изображений (PNG, JPG) и HTML-страниц можно использовать элемент формы ПолеHTMLДокумента. Данные передаются непосредственно в поле без сохранения на диск (например, через адрес во временном хранилище 1С или Data URI).
Текстовые и табличные данные можно загружать напрямую в объекты ТекстовыйДокумент или ТабличныйДокумент и выводить их во встроенных редакторах 1С.
Двоичные данные отдаются клиенту по запросу через HTTP-сервис конфигурации. Формируется временная URL-ссылка, по которой браузер или внешний просмотрщик получает поток данных по HTTP-протоколу, избегая явного сохранения файла клиентским кодом 1С.
В веб-клиенте передача файла пользователю реализуется через метод НачатьСкачиваниеФайла(). В этом случае браузер самостоятельно принимает поток данных и либо предлагает пользователю сохранить его, либо открывает во встроенном просмотрщике браузера (например, для PDF-файлов).
Проанализируем вторую проблему: при передаче HTML-кода из строки в элемент формы ПолеHTMLДокумента нажатие на внутренние ссылки (например, <a href="#section1">) приводит к ошибке или открытию пустой страницы.
Причина поведения: Когда HTML-документ формируется из строки кода в памяти, у него отсутствует базовый адрес (Base URI). Движок отображения (WebKit или Chromium) пытаются сопоставить относительную ссылку #section1 со служебным адресом 1С (вида e1c://... или v8config://...), из-за чего навигация ломается.
Выясним, как устранить эту проблему несколькими способами:
Самый простой способ — добавить в блок <head> загружаемого HTML-документа тег <base> с условным базовым адресом. Это позволяет браузерному движку корректно определять якоря внутри документа.
ТекстHTML = "<html>
|head>
| <base href=""about:blank"">
|</head>
|<body>
| <a href=""#anchor1"">Перейти к разделу 1</a>
| <div style=""height:1000px;""></div>
| <h2 id=""anchor1"">Раздел 1</h2>
|</body>
|</html>";
Элементы.ПолеHTML.УстановитьТекст(ТекстHTML);
Вместо стандартной навигации по гиперссылкам href="#id" можно использовать обработку нажатия через JavaScript-метод scrollIntoView(), который отменяет стандартный переход браузера:
<a href="#" onclick="document.getElementById('anchor1').scrollIntoView(); return false;">Перейти к разделу 1</a>
Если HTML-код нельзя редактировать на этапе формирования, можно перехватить нажатие на гиперссылку в событии элемента формы ПолеHTMLДокумента:
&НаКлиенте
Процедура ПолеHTMLПриНажатии(Элемент, ДанныеСобытия, Отказ)
// Проверяем, является ли ссылка внутренним якорем
Если ДанныеСобытия <> Неопределено И Найти(ДанныеСобытия.Href, "#") > 0 Тогда
Отказ = Истина; // Отменяем штатную навигацию 1С
// Получаем идентификатор якоря и выполняем прокрутку через DOM
ИмяЯкоря = Сред(ДанныеСобытия.Href, Найти(ДанныеСобытия.Href, "#") + 1);
Элемент.Документ.getElementById(ИмяЯкоря).scrollIntoView();
КонецЕсли;
КонецПроцедуры
Подводя итог: открывать файлы во внешних приложениях без записи во временный каталог на диске невозможно в силу архитектурных ограничений ОС и требований безопасности. Для работы в веб-клиенте следует использовать штатные просмотрщики 1С, HTTP-сервисы или скачивание файлов, а проблемы навигации в HTML-документах решает явное указание тега <base> или перехват событий DOM.