Как правильно проверить существование файла на сервере асинхронно и передать его на клиент в 1С

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

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

Почему АПК требует асинхронности на сервере?

Проанализируем ситуацию: разработчик пишет код для проверки файла на сервере, используя привычный метод Файл.Существует() или НайтиФайлы(). Однако при проверке через АПК система выдает предупреждение. Почему так происходит? Рассмотрим основные причины:

  1. Унификация программного интерфейса: Фирма «1С» стремится к тому, чтобы программный код был максимально универсальным. Использование асинхронных методов (Promises/Обещания) позволяет коду выглядеть одинаково как на клиенте, так и на сервере.
  2. Подготовка к будущим изменениям платформы: Платформа развивается в сторону неблокирующих операций. Даже если сейчас выполнение на сервере кажется синхронным, использование НайтиФайлыАсинх() гарантирует, что код будет соответствовать современным стандартам разработки.
  3. Работа в веб-клиенте: Веб-клиент накладывает жесткие ограничения на блокирующие вызовы. Использование единого асинхронного стиля упрощает отладку и поддержку решений, работающих в разных средах.

Реализация асинхронной проверки файла с помощью оператора Ждать

Разберем по шагам, как переписать старый синхронный код под требования АПК. Вместо создания объекта Файл и вызова метода Существует(), мы будем использовать глобальный метод НайтиФайлыАсинх(). Посмотрим на пример реализации на стороне сервера:


// Пример асинхронной проверки на сервере (в контексте серверной процедуры)
&НаСервере
Функция ПроверитьНаличиеЛогаНаСервере(ПолныйПутьКФайлу)
    
    // Вместо синхронного НайтиФайлы() используем асинхронный аналог
    // На сервере мы можем использовать оператор Ждать, чтобы дождаться результата
    Обещание = НайтиФайлыАсинх(ПолныйПутьКФайлу);
    МассивФайлов = Ждать Обещание;
    
    Если МассивФайлов.Количество() > 0 Тогда
        Возврат Истина;
    Иначе
        Возврат Ложь;
    КонецЕсли;
    
КонецФункции

Проанализируем этот код. Оператор Ждать приостанавливает выполнение текущего потока до тех пор, пока Обещание (результат работы НайтиФайлыАсинх) не будет выполнено. Это позволяет писать код в «линейном» стиле, при этом формально соблюдая требования асинхронности.

Проблема использования ЗапуститьПриложение на сервере

Часто разработчики совершают ошибку, пытаясь вызвать ЗапуститьПриложение() непосредственно в серверном коде, чтобы открыть лог-файл. Выясним причину, почему это не работает. Когда код выполняется на сервере, команда запуска приложения срабатывает в контексте операционной системы сервера, часто под служебной учетной записью (например, USR1CV8). Пользователь на своем компьютере ничего не увидит, а на сервере может зависнуть «фантомный» процесс текстового редактора.

Правильный алгоритм действий всегда должен быть следующим:

  1. Проверить наличие файла на сервере.
  2. Поместить файл во временное хранилище.
  3. Передать адрес хранилища на клиент.
  4. На клиенте получить файл и запустить его.

Передача файла с сервера на клиент через временное хранилище

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


&НаКлиенте
Асинх Процедура ПроверитьИОткрытьЛог(Команда)
    
    РезультатПроверки = ПроверитьНаличиеЛогаНаСервере(ПутьКФайлу);
    
    Если РезультатПроверки.ФайлНайден Тогда
        // Получаем файл с сервера во временное хранилище
        АдресХранилища = ПолучитьФайлВХранилище(ПутьКФайлу);
        
        // Получаем файл из хранилища на локальный диск клиента
        ОписаниеФайла = Новый ОписаниеПередаваемогоФайла(ПутьКФайлу);
        ПолученныеФайлы = Ждать ПолучитьФайлыАсинх(АдресХранилища, , Истина);
        
        Если ПолученныеФайлы <> Неопределено Тогда
            // Теперь файл на клиенте, его можно открыть
            ЗапуститьПриложение(ПолученныеФайлы[0].Имя);
        КонецЕсли;
    Иначе
        Сообщить("Файл лога не найден на сервере.");
    КонецЕсли;
    
КонецПроцедуры

&НаСервере
Функция ПолучитьФайлВХранилище(ПолныйПутьКФайлу)
    ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПолныйПутьКФайлу);
    Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, ЭтаФорма.УникальныйИдентификатор);
КонецФункции

Работа с большими файлами: как избежать Out of Memory

Разберем ситуацию, упомянутую в обсуждении: что делать, если лог-файл весит несколько гигабайт? Использование Новый ДвоичныеДанные(Путь) загрузит весь файл в оперативную память сервера. Если несколько пользователей одновременно попытаются открыть такие логи, сервер может упасть с ошибкой Out of Memory.

Для решения этой проблемы проанализируем современные возможности платформы по потоковой обработке данных. Начиная с версии 8.3.9, в 1С доступны объекты ЧтениеДанных и ФайловыйПоток.

Если файл слишком велик, вместо передачи всего файла целиком, лучше прочитать только его «хвост» (последние несколько тысяч строк) и передать только этот фрагмент. Посмотрим, как это реализовать:


&НаСервере
Функция ПрочитатьПоследниеСтрокиЛога(ПутьКФайлу, РазмерБуфераКБ = 100)
    
    ФайлЛога = Новый Файл(ПутьКФайлу);
    РазмерФайла = ФайлЛога.Размер();
    
    // Если файл меньше буфера, читаем целиком
    // Если больше — отступаем от конца
    Смещение = Макс(0, РазмерФайла - (РазмерБуфераКБ * 1024));
    
    Поток = Новый ФайловыйПоток(ПутьКФайлу, РежимОткрытияФайла.Открыть, ДоступКФайлу.Чтение);
    Поток.Перейти(Смещение, РежимПеремещенияВПотоке.Начало);
    
    Чтение = Новый ЧтениеДанных(Поток);
    Результат = Чтение.ПрочитатьСимволы(); // Читаем до конца потока
    
    Поток.Закрыть();
    
    Возврат ПоместитьВоВременноеХранилище(Результат, ЭтаФорма.УникальныйИдентификатор);
КонецФункции

Важный момент: При работе с потоками мы не загружаем файл в память целиком. Мы перемещаем указатель чтения в нужную позицию и считываем только требуемый фрагмент. Это позволяет безопасно обрабатывать логи любого объема.

Резюме и рекомендации

Подводя итог нашему анализу, сформулируем основные правила работы с файлами на сервере в современных конфигурациях:

Следуя этим правилам, вы не только успешно пройдете автоматизированную проверку конфигурации, но и создадите отказоустойчивое решение, которое не будет потреблять лишние ресурсы сервера при работе с большими объемами данных. Для дополнительного контроля качества кода можно использовать инструмент для анализа конфигураций и расширений на наличие ошибок.

← На главную