При работе с внешними ресурсами, такими как FTP-серверы, разработчики часто сталкиваются с проблемой: метод КопироватьФайл() или его аналоги «замораживают» клиентское приложение до завершения процесса загрузки. Если файл весит несколько сотен мегабайт, пользователь видит неактивное окно программы, что создает впечатление зависания системы. В этой статье мы подробно разберем, как реализовать загрузку файлов на сторону клиента, сохранив отзывчивость интерфейса и обеспечив визуализацию прогресса.
Важно сразу определить ограничение: мы рассматриваем именно загрузку на сторону клиента. Вариант с фоновым заданием на сервере часто отклоняется, так как перекачка больших файлов сначала на сервер, а затем с сервера на клиент создает двойную нагрузку на сетевой канал предприятия и может привести к деградации производительности для всех пользователей.
Начиная с версии платформы 8.3.18, фирма «1С» внедрила механизм асинхронности на основе обещаний (Promises) и ключевого слова Ждать (Await). Это наиболее современный и правильный способ решения проблемы блокировки потока интерфейса.
Рассмотрим применение метода КопироватьФайлАсинх(). В отличие от старых методов с использованием ОписаниеОповещения, данный подход позволяет писать код в линейном стиле, при этом выполнение кода в основном потоке будет приостановлено без блокировки отрисовки окон.
Разберем пример реализации:
&НаКлиенте
Асинх Процедура СкачатьФайлАсинхронно(ИмяФайлаИсточника, ИмяФайлаПриемника)
Попытка
// Интерфейс не блокируется, пользователь может двигать окно
Ждать КопироватьФайлАсинх(ИмяФайлаИсточника, ИмяФайлаПриемника);
ПоказатьОповещениеПользователя("Загрузка завершена", , ИмяФайлаПриемника);
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка при копировании: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
Главное преимущество этого метода — простота. Однако у него есть существенный минус: стандартный метод КопироватьФайлАсинх() не предоставляет встроенного события для отображения прогресса в процентах (0–100%).
Для более тонкой настройки работы с протоколом FTP рекомендуется использовать специализированный объект FTPСоединение. У него также существуют асинхронные аналоги методов получения файлов. Проанализируем, как это работает через НачатьПолучение().
Посмотрим на пример кода, который инициирует загрузку в неблокирующем режиме:
&НаКлиенте
Процедура НачатьЗагрузкуСFTP()
ПараметрыСоединения = Новый FTPСоединение("ftp.example.com", 21, "Логин", "Пароль");
Оповещение = Новый ОписаниеОповещения("ЗагрузкаЗавершена", ЭтотОбъект);
// Метод не ждет окончания, а сразу возвращает управление интерфейсу
ПараметрыСоединения.НачатьПолучение(Оповещение, "remote_file.zip", "C:\LocalFiles\file.zip");
Состояние("Файл загружается в фоновом режиме...");
КонецПроцедуры
&НаКлиенте
Процедура ЗагрузкаЗавершена(Результат, ДополнительныеПараметры) Экспорт
ТекстСообщения = "Файл успешно получен!";
ПоказатьОповещениеПользователя(ТекстСообщения);
КонецПроцедуры
Хотя этот метод лучше справляется с отзывчивостью, вопрос индикации прогресса остается открытым. Чтобы решить его внутри 1С, нам придется прибегнуть к хитростям.
Если нам критически важно показывать пользователю, сколько мегабайт уже скачано, мы можем использовать комбинацию асинхронной загрузки и периодической проверки размера локального файла. Выясним алгоритм действий:
FTPСоединение.Размер().Файл.Размер().Индикатор на форме.Проанализируем пример кода для обновления прогресс-бара:
&НаКлиенте
Перем РазмерФайлаНаСервере;
&НаКлиенте
Процедура ОбновитьИндикаторЗагрузки()
ЛокальныйФайл = Новый Файл("C:\LocalFiles\file.zip");
Если ЛокальныйФайл.Существует() Тогда
ТекущийРазмер = ЛокальныйФайл.Размер();
Процент = (ТекущийРазмер / РазмерФайлаНаСервере) * 100;
ЭтаФорма.ИндикаторЗагрузки = Процент;
КонецЕсли;
КонецПроцедуры
Для очень больших файлов и обеспечения максимальной стабильности интерфейса эффективно использовать внешние системные инструменты. Современные ОС Windows (10 и 11) уже включают в себя мощную консольную утилиту curl.
Преимущество этого метода заключается в том, что загрузка происходит в полностью независимом процессе. Мы можем запустить его скрыто, и 1С вообще не будет тратить ресурсы на обработку потока байтов.
Разберем пример вызова через ЗапуститьПриложение():
&НаКлиенте
Процедура СкачатьЧерезCurl()
Команда = "curl -u user:password ftp://example.com/file.zip -o C:\temp\file.zip";
// Запускаем процесс, не дожидаясь завершения (ДождатьсяЗавершения = Ложь)
ЗапуститьПриложение(Команда, , Ложь);
// Можно периодически проверять наличие файла или использовать лог-файл curl
КонецПроцедуры
Если нужно показать окно консоли с нативным прогрессом curl, установите параметр СпособОтображенияОкна в значение Максимизированное или Нормальное.
Для реализации «честного» индикатора внутри 1С без внешних окон можно воспользоваться объектами Windows WinHttp.WinHttpRequest.5.1 и ADODB.Stream. Этот метод позволяет читать данные порциями в цикле.
Посмотрим на логику процесса:
ADODB.Stream.ОбработкаПрерыванияПользователя(), чтобы 1С могла перерисовать интерфейс на каждой итерации.Важный момент: использование COM-объектов ограничивает кроссплатформенность (не будет работать на Linux-клиентах), но для Windows-инфраструктуры это дает самый полный контроль над процессом.
Выбор метода зависит от версии вашей платформы и требований к интерфейсу:
КопироватьФайлАсинх().curl, чтобы минимизировать влияние на стабильность сеанса 1С.Помните, что при работе с FTP на стороне клиента необходимо убедиться, что у пользователя есть права на запись в целевой каталог и настройки брандмауэра позволяют устанавливать исходящие соединения по соответствующим портам.