Как программно заполнить поле выбора файла в HTML-документе 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В современной разработке на платформе 1С часто возникает задача интеграции с внешними веб-сервисами через «Поле HTML документа». Одной из самых трудоемких проблем является программная загрузка файла на сторонний сайт. Типичная ситуация: на форме сайта присутствует кнопка «Выбрать файл», при нажатии на которую открывается стандартное диалоговое окно ОС. Наша задача — автоматизировать этот процесс, исключив ручной выбор файла пользователем.

Проанализируем, почему стандартный подход через прямое присвоение значения свойству value объекта input не работает, и рассмотрим эффективные методы решения этой задачи.

Причина ограничения: Security Sandbox

Прежде всего, разберем фундаментальную причину сложности. Все современные браузеры, включая движок WebKit/Chromium, используемый в платформе 1С 8.3, работают в режиме «песочницы» (Security Sandbox). По соображениям безопасности JavaScript не имеет права программно устанавливать путь к локальному файлу в элементе input type="file". Если бы это было возможно, любой вредоносный сайт мог бы незаметно для пользователя скачать конфиденциальные документы с его компьютера при простом посещении страницы.

В контексте 1С это означает, что попытка выполнить код типа Инпут.value = "C:\MyFile.txt" будет либо проигнорирована браузером, либо вызовет ошибку доступа. Поэтому нам необходимо рассмотреть альтернативные пути обхода этого ограничения.

Способ №1: Имитация HTTP POST-запроса через 1С

Самым надежным и архитектурно правильным способом является полный отказ от манипуляций с экранными формами сайта в пользу прямой отправки данных на сервер. Если мы знаем, какой файл нужно отправить, мы можем сформировать HTTP-запрос средствами 1С, имитируя действия браузера при нажатии кнопки «Отправить».

Рассмотрим по шагам, как это реализовать:

  1. Анализ запроса: Откроем сайт в браузере Chrome, нажмем F12 и перейдем на вкладку Network. Выполним загрузку файла вручную. Нам нужно найти запрос с типом POST и заголовком Content-Type: multipart/form-data.
  2. Сбор параметров: Зафиксируем адрес (URL), заголовки (Headers) и структуру тела запроса (Payload). Обратите внимание на Cookie — их нужно будет передать из «Поля HTML документа» в наш объект HTTPСоединение.
  3. Программная реализация: Используем объекты HTTPСоединение и HTTPЗапрос.

Пример кода для формирования multipart-запроса:


// Подготовим границы данных
Разделитель = "----" + Формат(ТекущаяДата(), "ДФ=yyyyMMddHHmmss");
ИмяФайла = "document.pdf";
ДвоичныеДанныеФайла = Новый ДвоичныеДанные("C:\Temp\document.pdf");

// Формируем тело запроса
Поток = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(Поток);
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""PKeyFileInput""; filename=""" + ИмяФайла + """");
ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/octet-stream");
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.Записать(ДвоичныеДанныеФайла);
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + "--");
ЗаписьДанных.Закрыть();

РезультатТело = Поток.ЗакрытьИПолучитьДвоичныеДанные();

// Отправка
Соединение = Новый HTTPСоединение("example.com", , , , , 60);
Запрос = Новый HTTPЗапрос("/upload");
Запрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Разделитель);
Запрос.УстановитьТелоИзДвоичныхДанных(РезультатТело);

Ответ = Соединение.ОтправитьДляОбработки(Запрос);

После успешной отправки мы можем обновить содержимое Поля HTML документа, чтобы пользователь увидел результат загрузки, как если бы он сделал это вручную.

Способ №2: Использование системного объекта WScript.Shell (SendKeys)

Если сайт использует сложную логику на Angular (как в нашем примере с атрибутами _ngcontent-c8) или React, и формирование прямого запроса слишком трудоемко, можно воспользоваться «эмуляцией действий пользователя». Этот метод считается менее стабильным, но иногда он является единственным выходом.

Суть метода заключается в том, чтобы программно нажать на кнопку выбора файла, дождаться появления диалогового окна ОС и «напечатать» в него путь к файлу с последующим нажатием Enter.

Проанализируем последовательность действий:

  1. Программно вызываем метод click() у элемента button или скрытого input.
  2. Используем объект WScript.Shell для передачи строки пути.

Пример реализации на стороне 1С:


// 1. Активируем кнопку выбора файла на странице
Инпут = ЭтаФорма.ПодчиненныеЭлементы.HTML.Документ.getElementById("PKeyFileInput");
Инпут.click(); // Открывается системное окно выбора файла

// 2. Используем WScript.Shell для ввода пути
// Важно: окно должно быть в фокусе
WshShell = Новый COMОбъект("WScript.Shell");
// Небольшая пауза, чтобы окно успело открыться
Таймер = ТекущаяДата() + 1;
Пока ТекущаяДата() < Таймер Цикл
КонецЦикла;

WshShell.SendKeys("C:\Data\upload.txt");
WshShell.SendKeys("{ENTER}");

Важное примечание: Метод SendKeys сильно зависит от текущей раскладки клавиатуры и фокуса окна. Если в момент выполнения кода пользователь переключит окно, путь к файлу может быть напечатан в другом месте. Для повышения надежности рекомендуется использовать метод AppActivate объекта WScript.Shell, чтобы принудительно вернуть фокус окну выбора файла.

Способ №3: Внедрение JavaScript и работа с объектом DataTransfer

В современных версиях платформы 1С, где используется актуальный движок браузера, можно попробовать применить JS-хак с использованием объекта DataTransfer. Это позволяет программно создать список файлов и «подсунуть» его в коллекцию files элемента input.

Выясним, как это работает. Мы можем подготовить файл в формате Base64 в 1С, передать его в JavaScript, превратить в Blob и затем в объект File. Это требует продвинутых навыков работы с JS.

Пример логики скрипта, который можно выполнить через ЭтаФорма.ПодчиненныеЭлементы.HTML.Документ.parentWindow.eval():


function setFileInput(base64Content, fileName) {
    const byteCharacters = atob(base64Content);
    const byteNumbers = new Array(byteCharacters.length);
    for (let i = 0; i < byteCharacters.length; i++) {
        byteNumbers[i] = byteCharacters.charCodeAt(i);
    }
    const byteArray = new Uint8Array(byteNumbers);
    const blob = new Blob([byteArray], {type: 'application/octet-stream'});
    
    const file = new File([blob], fileName);
    const dataTransfer = new DataTransfer();
    dataTransfer.items.add(file);
    
    const fileInput = document.getElementById('PKeyFileInput');
    fileInput.files = dataTransfer.files;
    
    // Генерируем событие изменения, чтобы Angular узнал о файле
    const event = new Event('change', { bubbles: true });
    fileInput.dispatchEvent(event);
}

Этот метод является наиболее элегантным, так как он не требует взаимодействия с интерфейсом ОС и работает полностью внутри контекста браузера. Однако он требует, чтобы движок Chromium в 1С поддерживал конструктор DataTransfer.

Резюме

Подводя итог, выбор метода зависит от конкретной задачи:

При работе с Angular-сайтами всегда обращайте внимание на события. Простого заполнения поля недостаточно — всегда вызывайте событие change или input, чтобы фреймворк «увидел» изменения в модели данных страницы.

← На главную