В современной разработке на платформе 1С часто возникает задача интеграции с внешними веб-сервисами через «Поле HTML документа». Одной из самых трудоемких проблем является программная загрузка файла на сторонний сайт. Типичная ситуация: на форме сайта присутствует кнопка «Выбрать файл», при нажатии на которую открывается стандартное диалоговое окно ОС. Наша задача — автоматизировать этот процесс, исключив ручной выбор файла пользователем.
Проанализируем, почему стандартный подход через прямое присвоение значения свойству value объекта input не работает, и рассмотрим эффективные методы решения этой задачи.
Прежде всего, разберем фундаментальную причину сложности. Все современные браузеры, включая движок WebKit/Chromium, используемый в платформе 1С 8.3, работают в режиме «песочницы» (Security Sandbox). По соображениям безопасности JavaScript не имеет права программно устанавливать путь к локальному файлу в элементе input type="file". Если бы это было возможно, любой вредоносный сайт мог бы незаметно для пользователя скачать конфиденциальные документы с его компьютера при простом посещении страницы.
В контексте 1С это означает, что попытка выполнить код типа Инпут.value = "C:\MyFile.txt" будет либо проигнорирована браузером, либо вызовет ошибку доступа. Поэтому нам необходимо рассмотреть альтернативные пути обхода этого ограничения.
Самым надежным и архитектурно правильным способом является полный отказ от манипуляций с экранными формами сайта в пользу прямой отправки данных на сервер. Если мы знаем, какой файл нужно отправить, мы можем сформировать HTTP-запрос средствами 1С, имитируя действия браузера при нажатии кнопки «Отправить».
Рассмотрим по шагам, как это реализовать:
F12 и перейдем на вкладку Network. Выполним загрузку файла вручную. Нам нужно найти запрос с типом POST и заголовком Content-Type: multipart/form-data.Cookie — их нужно будет передать из «Поля HTML документа» в наш объект HTTPСоединение.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 документа, чтобы пользователь увидел результат загрузки, как если бы он сделал это вручную.
Если сайт использует сложную логику на Angular (как в нашем примере с атрибутами _ngcontent-c8) или React, и формирование прямого запроса слишком трудоемко, можно воспользоваться «эмуляцией действий пользователя». Этот метод считается менее стабильным, но иногда он является единственным выходом.
Суть метода заключается в том, чтобы программно нажать на кнопку выбора файла, дождаться появления диалогового окна ОС и «напечатать» в него путь к файлу с последующим нажатием Enter.
Проанализируем последовательность действий:
click() у элемента button или скрытого input.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, чтобы принудительно вернуть фокус окну выбора файла.
В современных версиях платформы 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, чтобы фреймворк «увидел» изменения в модели данных страницы.