При разработке интерфейсов на управляемых формах 1С часто возникает необходимость отображать пользователю различные мультимедийные или документные файлы непосредственно в форме, не заставляя его скачивать их или открывать во внешних приложениях. Одним из мощных инструментов для этого является элемент формы ПолеHTMLДокумента, возможности которого позволяют создавать даже встраиваемые консоли кода. Однако, работа с ним имеет свои особенности, особенно когда речь заходит о загрузке и корректном отображении файлов разных типов.
Мы рассмотрим, как можно эффективно решить эту задачу, преодолевая стандартные ограничения безопасности браузеров и предоставляя пользователю удобный и функциональный интерфейс. Мы разберем подходы для изображений, документов PDF и Word, предложим конкретные примеры кода и объясним логику каждого шага.
Важно понимать, что ПолеHTMLДокумента в 1С фактически представляет собой встроенный в платформу веб-браузер (например, на базе компонентов Internet Explorer или Edge Chromium в зависимости от версии 1С и настроек ОС). Это означает, что все ограничения безопасности, присущие обычным браузерам, будут действовать и здесь. В частности, это касается доступа к локальным файлам по путям типа file:///, который часто бывает ограничен из соображений безопасности.
Прежде чем мы перейдем к конкретным решениям, давайте зафиксируем несколько ключевых принципов, которые помогут нам в работе:
<img> для изображений, <object> или <iframe> для PDF-документов.C:\temp\my_file.pdf) в атрибутах src или data чаще всего не сработает из-за ограничений безопасности, не позволяющих веб-странице (или встроенному браузеру) напрямую читать произвольные локальные файлы.Теперь давайте разберем конкретные реализации для каждого типа файлов.
Для отображения изображений (JPG, PNG, GIF и т.д.) оптимальным и самым простым способом является использование Base64-кодирования — для этого есть обработка предварительного просмотра картинок на форме 1С. Этот метод позволяет встроить двоичные данные изображения непосредственно в HTML-тег <img>. Таким образом, изображение становится частью самой веб-страницы, и встроенному браузеру не требуется обращаться к внешним файлам, что полностью обходит все ограничения безопасности.
Мы рассмотрим шаги, необходимые для реализации этого подхода:
Base64Строка().<img>, в котором указываем Base64-строку в атрибуте src в специальном формате data:image/[тип_изображения];base64,[Base64_строка].УстановитьHTML() элемента ПолеHTMLДокумента.Посмотрим на пример функции, которая преобразует файл в Base64-строку:
// Серверная функция или функция на клиенте, доступная для вызова с клиента
// Предполагается, что ПутьКФайлу - это путь к файлу на том же компьютере, где исполняется код (клиент или сервер).
Функция ПолучитьBase64ИзФайла(ПутьКФайлу) Экспорт
Попытка
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
Возврат Base64Строка(ДвоичныеДанные);
Исключение
Сообщить("Ошибка при получении Base64 из файла " + ПутьКФайлу + ": " + ОписаниеОшибки());
Возврат "";
КонецПопытки;
КонецФункции
Теперь, когда у нас есть функция для получения Base64-строки, мы можем использовать ее на клиенте для отображения изображения в HTML-поле. Допустим, у нас есть элемент ПолеHTMLДокумента на форме с именем Элементы.ПолеДляОтображения.
// Пример использования на клиенте после выбора файла или получения его пути
&НаКлиенте
Процедура ОтобразитьИзображениеВHTMLПоле(ПутьКФайлуИзображения)
Если ПутьКФайлуИзображения = Неопределено Тогда
Возврат;
КонецЕсли;
// Вызываем серверную функцию для получения Base64 строки (или клиентскую, если файл на клиенте)
Base64Изображение = ПолучитьBase64ИзФайла(ПутьКФайлуИзображения);
Если ПустаяСтрока(Base64Изображдение) Тогда
Сообщить("Не удалось получить Base64-строку изображения.");
Возврат;
КонецЕсли;
// Определяем тип изображения из расширения файла для корректного data URI
ТипИзображения = НСтр("ru = 'jpeg'"); // По умолчанию
Расширение = НРег(Прав(ПутьКФайлуИзображения, СтрДлина(ПутьКФайлуИзображения) - СтрНайти(ПутьКФайлуИзображения, ".")));
Если Расширение = "png" Тогда
ТипИзображения = НСтр("ru = 'png'");
ИначеЕсли Расширение = "gif" Тогда
ТипИзображения = НСтр("ru = 'gif'");
ИначеЕсли Расширение = "bmp" Тогда
ТипИзображения = НСтр("ru = 'bmp'");
КонецЕсли;
HTMLКод = "
<html>
<head>
<meta charset=""utf-8"">
<style>
body { margin: 0; padding: 0; overflow: auto; text-align: center; }
img { max-width: 100%; height: auto; display: block; margin: 0 auto; }
</style>
</head>
<body>
<img src=""data:image/" + ТипИзображения + ";base64," + Base64Изображение + """ alt=""Изображение"">
</body>
</html>";
Элементы.ПолеДляОтображения.УстановитьHTML(HTMLКод);
КонецПроцедуры
Данный подход прост в реализации и очень эффективен для изображений. При необходимости можно программно реализовать склейку нескольких картинок в одну перед их выводом. Однако, следует помнить, что Base64-строка увеличивает размер данных примерно на 33%, что влияет на производительность при больших объемах.
Для PDF-документов использование Base64-кодирования также возможно, но может быть неэффективным из-за размера файлов. PDF-файлы часто бывают значительно больше изображений. Чтобы избежать тормозов, можно использовать преобразование PDF в изображения или метод с использованием временных файлов на клиенте.
Суть метода заключается в следующем: мы сохраняем PDF-файл из 1С на диск клиента во временную папку, а затем указываем путь к этому временному файлу в HTML-теге <object> или <iframe>. Встроенный браузер обычно может отображать локальные PDF-файлы, если на компьютере установлен соответствующий плагин или браузер имеет встроенный просмотрщик PDF.
Разберем процесс по шагам:
ХранилищеЗначения) или из внешнего источника — для этого подойдёт модуль хранения присоединенных файлов на диске.Записать() объекта ДвоичныеДанные, мы сохраняем PDF-файл в специально созданный временный файл на клиентском компьютере.<object> или <iframe>, указывая в его атрибуте data (для <object>) или src (для <iframe>) полный путь к сохраненному временному PDF-файлу.Элементы.ПолеДляОтображения.УстановитьHTML().Рассмотрим пример кода для отображения PDF:
// Серверная функция для получения двоичных данных, если файл хранится в ИБ
// Например, файл хранится в реквизите "ДанныеФайла" типа ХранилищеЗначения
&НаСервере
Функция ПолучитьДвоичныеДанныеPDFССервера(ИдентификаторХранилища)
Если ИдентификаторХранилища = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
// Пример получения из хранилища, может быть другая логика
Данные = ИдентификаторХранилища.Получить();
Если ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда
Возврат Данные;
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
// Клиентская процедура для отображения PDF
&НаКлиенте
Процедура ОтобразитьPDFВHTMLПоле(ДвоичныеДанныеФайлаPDF)
Если ДвоичныеДанныеФайлаPDF = Неопределено Тогда
Сообщить("Двоичные данные PDF не получены.");
Возврат;
КонецЕсли;
ПутьКВременномуФайлу = "";
Попытка
// Создаем временный файл на клиенте с расширением .pdf
ПутьКВременномуФайлу = ПолучитьИмяВременногоФайла(".pdf");
ДвоичныеДанныеФайлаPDF.Записать(ПутьКВременномуФайлу);
// Формируем HTML-код для отображения PDF
// Используем <object> для встраивания PDF
// Можно также использовать <iframe src=""..."">
HTMLДокумент = "
<html>
<head>
<meta charset=""utf-8"">
<style>
body { margin: 0; padding: 0; overflow: hidden; }
object { display: block; width: 100%; height: 100vh; border: none; }
.fallback-text { font-family: sans-serif; padding: 20px; text-align: center; }
</style>
</head>
<body>
<object data=""file:///" + Заменить(ПутьКВременномуФайлу, "\", "/") + """ type=""application/pdf"">
<p class=""fallback-text"">Ваш браузер не поддерживает встроенные PDF-файлы.
Вы можете <a href=""file:///" + Заменить(ПутьКВременномуФайлу, "\", "/") + """ download>скачать PDF</a>.</p>
</object>
</body>
</html>";
Элементы.ПолеДляОтображения.УстановитьHTML(HTMLДокумент);
// Сохраняем путь к временному файлу, чтобы удалить его позже
// Например, в реквизит формы
// ЭтаФорма.ПутьКТекущемуВременномуФайлу = ПутьКВременномуФайлу;
Исключение
Сообщить("Ошибка при отображении PDF: " + ОписаниеОшибки());
Если Не ПустаяСтрока(ПутьКВременномуФайлу) И ФайлСуществует(ПутьКВременномуФайлу) Тогда
Попытка УдалитьФайлы(ПутьКВременномуФайлу); Исключение КонецПопытки;
КонецЕсли;
КонецПопытки;
КонецПроцедуры
Важные замечания по временным файлам для PDF:
ПриЗакрытии формы или с помощью регламентного задания, которое удаляет старые временные файлы. Пример очистки:
&НаКлиенте
Процедура ПриЗакрытии()
Если Не ПустаяСтрока(ЭтаФорма.ПутьКТекущемуВременномуФайлу) И ФайлСуществует(ЭтаФорма.ПутьКТекущемуВременномуФайлу) Тогда
Попытка
УдалитьФайлы(ЭтаФорма.ПутьКТекущемуВременномуФайлу);
Исключение
Сообщить("Не удалось удалить временный файл: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Заменить(ПутьКВременномуФайлу, "\", "/"). Для корректной работы в HTML-путях рекомендуется использовать прямые слэши, а префикс file:/// явно указывает на локальный файл.Если вы хотите избежать создания временных файлов на клиенте или вам требуется более универсальное решение, которое не зависит от настроек клиентского браузера и доступа к локальному диску, можно использовать HTTP-сервис 1С. Этот подход требует публикации вашей информационной базы на веб-сервере (Apache или IIS).
Идея состоит в следующем: вы создаете HTTP-сервис в конфигурации 1С, который будет обрабатывать запросы по определенному URL. Когда браузер (в данном случае, встроенное HTML-поле) запрашивает файл по этому URL, HTTP-сервис на стороне 1С извлекает двоичные данные файла (например, из хранилища 1С) и возвращает их в ответ. HTML-поле при этом получает данные напрямую с веб-сервера, что является стандартным и безопасным способом для браузеров.
Общие шаги:
/файл/{Идентификатор}.Идентификатор из URL. Этот идентификатор будет использоваться для получения нужного файла из базы данных (например, это может быть ссылка на элемент справочника "Файлы" или уникальный идентификатор временного хранилища).ОтветHTTP, устанавливаем ему соответствующий заголовок Content-Type (например, application/pdf) и записываем двоичные данные в тело ответа.<object> или <iframe> мы указываем URL к нашему HTTP-сервису: <object data="[адрес_веб-сервера]/[имя_базы]/hs/файлы/[идентификатор_файла]" type="application/pdf">.Это решение является более сложным в настройке, но предоставляет максимальную гибкость и безопасность, а также не требует манипуляций с локальными файлами на клиенте. Оно идеально подходит для корпоративных систем, где база 1С уже опубликована на веб-сервере.
Отображение документов Microsoft Word, Excel и других офисных форматов (.doc, .docx, .xls, .xlsx) напрямую в HTML-поле является наиболее сложной задачей, и для нее нет простого и универсального нативного решения, сравнимого с изображениями или PDF.
Причины:
Какие же есть варианты, если вам все же нужно каким-то образом работать с Word/Excel в 1С?
Это самый простой и надежный подход. Вместо попытки отображения, вы просто предоставляете пользователю ссылку для скачивания файла. Пользователь скачивает файл и открывает его в своем локальном приложении Office. Это работает всегда и не требует сложных решений.
&НаКлиенте
Процедура ПредложитьСкачатьФайл(ДвоичныеДанныеФайла, ИмяФайла)
Если ДвоичныеДанныеФайла = Неопределено Тогда
Сообщить("Двоичные данные файла не получены.");
Возврат;
КонецЕсли;
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(ИмяФайла);
Попытка
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
// Открытие диалога сохранения на клиенте
НачатьПомещениеФайлаНаКлиент(ОписаниеОповещения("ФайлПомещенНаКлиент", ЭтотОбъект), , ИмяВременногоФайла, Истина, ИмяФайла);
Исключение
Сообщить("Ошибка при подготовке файла к скачиванию: " + ОписаниеОшибки());
Если ФайлСуществует(ИмяВременногоФайла) Тогда
Попытка УдалитьФайлы(ИмяВременногоФайла); Исключение КонецПопытки;
КонецЕсли;
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура ФайлПомещенНаКлиент(РезультатПомещения, ДополнительныеПараметры) Экспорт
Если РезультатПомещения.СтатусВозврата Тогда
Сообщить("Файл успешно скачан.");
Иначе
Сообщить("Не удалось скачать файл: " + РезультатПомещения.СообщениеПользователю);
КонецЕсли;
// Очищаем временный файл, если он был создан
Если Не ПустаяСтрока(ДополнительныеПараметры) И ФайлСуществует(ДополнительныеПараметры) Тогда
Попытка УдалитьФайлы(ДополнительныеПараметры); Исключение КонецПопытки;
КонецЕсли;
КонецПроцедуры
Существуют онлайн-сервисы, такие как Google Docs Viewer или Microsoft Office Online Viewer, которые могут отображать документы Word/Excel по публичной ссылке. Вы могли бы загрузить документ на такой сервис (или сделать его доступным через ваш HTTP-сервис 1С) и затем встроить просмотрщик в <iframe>:
HTMLДокумент = "
<iframe src=""https://docs.google.com/viewer?url=[URL_к_вашему_файлу.docx]&embedded=true""
width=""100%"" height=""500px"" frameborder=""0"">
</iframe>";
Элементы.ПолеДляОтображения.УстановитьHTML(HTMLДокумент);
Недостатки: Этот подход требует, чтобы ваш файл был доступен по публичной или хотя бы внутренней корпоративной ссылке, а также зависит от стабильности и доступности сторонних сервисов. Возможны вопросы конфиденциальности, если документ содержит чувствительную информацию.
Существуют сложные JavaScript-библиотеки, которые могут пытаться парсить и рендерить содержимое Word/Excel документов на стороне клиента (например, библиотеки для работы с форматом OOXML). Однако это очень трудоемкий путь, требующий глубоких знаний веб-разработки и интеграции с 1С, а результат может быть неидеален (потеря форматирования, большой размер библиотек).
Вывод для Word/Excel: Для этих типов документов наиболее практичным решением является предоставление возможности скачивания. Если же требуется просмотр отчетов, можно применить конвертацию отчетов в графический формат. Попытки встроить Office-файлы в HTML-поле напрямую сопряжены с большими трудностями.
Автор исходного вопроса также упомянул желаемый функционал Drag-and-Drop или кнопку "Загрузить". Давайте кратко рассмотрим, как это может быть интегрировано с описанными выше решениями.
Это самый простой способ получить путь к файлу на клиенте. Мы используем стандартный механизм 1С для выбора файла.
&НаКлиенте
Процедура КомандаЗагрузитьФайл(Команда)
// Открытие стандартного диалога выбора файла
ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогВыбораФайла.Заголовок = "Выберите файл для отображения";
// Укажите фильтр по типам файлов, если нужно
ДиалогВыбораФайла.Фильтр = "Изображения (*.jpg;*.jpeg;*.png;*.gif)|*.jpg;*.jpeg;*.png;*.gif|PDF документы (*.pdf)|*.pdf|Все файлы (*.*)|*.*";
ДиалогВыбораФайла.Расширение = "jpg"; // Расширение по умолчанию
ДиалогВыбораФайла.МножественныйВыбор = Ложь;
Если ДиалогВыбораФайла.Выбрать() Тогда
ПутьКФайлу = ДиалогВыбораФайла.ПолноеИмяФайла;
ОбработатьВыбранныйФайл(ПутьКФайлу);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьВыбранныйФайл(ПутьКФайлу)
Если ПустаяСтрока(ПутьКФайлу) Тогда
Возврат;
КонецЕсли;
Расширение = НРег(Прав(ПутьКФайлу, СтрДлина(ПутьКФайлу) - СтрНайти(ПутьКФайлу, ".")));
Если Расширение В ("jpg", "jpeg", "png", "gif", "bmp") Тогда
ОтобразитьИзображениеВHTMLПоле(ПутьКФайлу);
ИначеЕсли Расширение = "pdf" Тогда
// Для PDF нужно сначала получить двоичные данные, если ОтобразитьPDFВHTMLПоле их принимает
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
ОтобразитьPDFВHTMLПоле(ДвоичныеДанные);
ИначеЕсли Расширение В ("doc", "docx", "xls", "xlsx") Тогда
// Для Word/Excel - только скачивание
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
ИмяФайла = ИмяФайлаИзПолногоПути(ПутьКФайлу);
ПредложитьСкачатьФайл(ДвоичныеДанные, ИмяФайла);
Сообщить("Документы Word/Excel не могут быть отображены в HTML-поле. Предлагается скачивание.");
Иначе
Сообщить("Тип файла '" + Расширение + "' не поддерживается для отображения. Файл будет предложен для скачивания.");
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлу);
ИмяФайла = ИмяФайлаИзПолногоПути(ПутьКФайлу);
ПредложитьСкачатьФайл(ДвоичныеДанные, ИмяФайла);
КонецЕсли;
КонецПроцедуры
// Вспомогательная функция (может быть в общем модуле или в модуле формы)
&НаКлиенте
Функция ИмяФайлаИзПолногоПути(ПолныйПуть)
ИмяФайла = ПолныйПуть;
ИндексСлэша = СтрНайти(ИмяФайла, "\", , -1);
Если ИндексСлэша > 0 Тогда
ИмяФайла = Сред(ИмяФайла, ИндексСлэша + 1);
КонецЕсли;
ИндексСлэша = СтрНайти(ИмяФайла, "/", , -1);
Если ИндексСлэша > 0 Тогда
ИмяФайла = Сред(ИмяФайла, ИндексСлэша + 1);
КонецЕсли;
Возврат ИмяФайла;
КонецФункции
Реализация Drag-and-Drop в ПолеHTMLДокумента сложнее, так как требует взаимодействия с JavaScript внутри HTML-поля и обратной связи с контекстом 1С. Это выходит за рамки простого использования УстановитьHTML(). Вам понадобится:
ПолеHTMLДокумента, создать область, которая будет слушать события dragover, dragleave, drop.drop: В JavaScript-обработчике события drop получить доступ к перетащенным файлам через объект event.dataTransfer.files.File не могут быть напрямую переданы. Один из подходов – читать содержимое файла в Base64 с помощью FileReader в JavaScript, а затем передавать эту Base64-строку в 1С через механизм Оповестить() или ВнешниеСобытия().Такая реализация требует значительных усилий и знаний JavaScript, поэтому обычно для Drag-and-Drop используют стандартные элементы 1С, если они доступны, или специализированные внешние компоненты, которые упрощают такое взаимодействие.
Попытка-Исключение при работе с файлами и внешними вызовами, чтобы ваша программа была устойчивой к ошибкам файловой системы, сетевым проблемам или отсутствию прав доступа.img { max-width: 100%; height: auto; }).Таким образом, мы видим, что хотя отображение различных типов файлов в HTML-поле 1С и имеет свои нюансы, платформа предоставляет достаточно инструментов для эффективного решения этой задачи. Выбирайте подход, который наилучшим образом соответствует требованиям вашего проекта по сложности, производительности и удобству использования.