В практической работе разработчика 1С часто возникает задача автоматической загрузки данных из внешних систем. Классический сценарий: сторонняя программа (например, WMS, CRM или сайт) выгружает файл обмена в общий сетевой каталог, а решение на базе 1С должно его подхватить и обработать. Однако на практике время появления этого файла часто сдвигается из-за задержек на стороне отправляющей системы. Если регламентное задание 1С настроено на строго фиксированное время, оно может отработать «вхолостую» и завершиться до фактического появления файла.
В рамках данной статьи мы подробно разберем, как спроектировать отказоустойчивый механизм импорта файлов, который будет корректно ожидать их появления, обрабатывать различные исключительные ситуации (например, когда файл еще копируется сторонней системой) и отправлять уведомления ответственным лицам.
Это самый простой и быстрый способ реализации ожидания, предложенный участниками обсуждения. Платформа 1С:Предприятие 8 поддерживает автоматический перезапуск регламентных заданий в случае возникновения ошибок в процессе их выполнения.
Рассмотрим алгоритм настройки данного механизма по шагам:
РегламентныеЗадания.Посмотрим на пример программной реализации этого подхода:
Процедура ВыполнитьЗагрузкуФайла() Экспорт
ПутьККаталогу = "D:\Exchange\Input\";
МаскаФайла = "*_nonDMA.xls";
НайденныеФайлы = НайтиФайлы(ПутьККаталогу, МаскаФайла);
Если НайденныеФайлы.Количество() = 0 Тогда
// Файла нет. Вызываем исключение, чтобы платформа перезапустила задание по расписанию повтора
ВызватьИсключение "Файл для импорта не найден в каталоге " + ПутьККаталогу + ". Повторная попытка через 5 минут.";
КонецЕсли;
// Если файл найден, выполняем стандартную загрузку
Для Каждого Файл Из НайденныеФайлы Цикл
ОбработатьФайлИмпорта(Файл.ПолноеИмя);
КонецЦикла;
КонецПроцедуры
Анализ преимуществ и недостатков Варианта 1:
Данный подход привлекает своей простотой. Нам не нужно создавать дополнительные структуры данных для хранения состояний обмена. Однако у него есть существенные архитектурные минусы. Во-первых, при каждом неудачном поиске файла ЖурналРегистрации будет забиваться записями об ошибках (с красной иконкой), что усложнит мониторинг реальных системных сбоев. Во-вторых, этот механизм предназначен для обработки случайных технических сбоев (например, временных блокировок СУБД), а не для штатного ожидания бизнес-событий.
Более надежный и гибкий подход заключается в частом запуске регламентного задания по обычному расписанию (например, каждые 10 минут в интервале с 4:20 до 7:00) и сохранении статуса загрузки в специальный РегистрСведений. Если за сегодня файл уже был успешно загружен, регламентное задание будет мгновенно завершаться без ошибок и лишних действий.
Разберем создание структуры данных. Создадим непериодический регистр сведений ИсторияЗагрузкиФайлов со следующими реквизитами:
ДатаИмпорта (Тип: Дата, только дата).Статус (Тип: Булево или ПеречислениеСсылка.СтатусыИмпорта — например, "Загружен", "Ошибка").ИмяФайла (Тип: Строка).ДатаВремяВыполнения (Тип: Дата, дата и время).Проанализируем программный код, реализующий этот алгоритм. Мы будем использовать метод НайтиФайлы, выполнять проверку наличия записи в регистре, а также обрабатывать файлы корректно:
Процедура ВыполнитьЗагрузкуФайлаСКонтролемСтатуса() Экспорт
ТекущаяДатаСреза = НачалоДня(ТекущаяДата());
// 1. Проверяем, был ли уже выполнен импорт за сегодня
Если ИмпортЗаСегодняВыполнен(ТекущаяДатаСреза) Тогда
// Завершаем работу тихо и штатно, чтобы не перегружать сервер
Возврат;
КонецЕсли;
// 2. Ищем файл на диске
ПутьККаталогу = "D:\Exchange\Input\";
МаскаФайла = "*_nonDMA.xls";
НайденныеФайлы = НайтиФайлы(ПутьККаталогу, МаскаФайла);
Если НайденныеФайлы.Количество() = 0 Тогда
// Файла еще нет — это нормальная рабочая ситуация.
// Записываем информационное сообщение в лог и выходим без ошибки.
ЗаписатьДанныеВЛог("Файл импорта не найден. Ожидаем следующий запуск по расписанию.");
Возврат;
КонецЕсли;
// 3. Файл найден. Начинаем обработку
Для Каждого Файл Из НайденныеФайлы Цикл
// Защита от незавершенного копирования
Если ФайлЗанятДругимПроцессом(Файл.ПолноеИмя) Тогда
ЗаписатьДанныеВЛог("Файл " + Файл.Имя + " занят другим процессом. Возможно, он еще копируется. Пропускаем.");
Продолжить;
КонецЕсли;
Попытка
НачатьТранзакцию();
ОбработатьФайлИмпорта(Файл.ПолноеИмя);
// Фиксируем успешное выполнение в регистре сведений
ЗаписатьСтатусЗагрузки(ТекущаяДатаСреза, Файл.Имя, Истина);
// Переносим обработанный файл в папку архива, чтобы не обрабатывать его повторно
ПереместитьФайлВАрхив(Файл);
ЗафиксироватьТранзакцию();
ЗаписатьДанныеВЛог("Файл " + Файл.Имя + " успешно загружен.");
Исключение
ОтменитьТранзакцию();
ЗаписатьДанныеВЛог("Ошибка при обработке файла " + Файл.Имя + ": " + ОписаниеОшибки(), Истина);
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Один из главных «подводных камней», упомянутых в обсуждении на форуме — ситуация, когда внешняя система начала копировать файл в папку обмена, но процесс еще не завершился. В этот момент 1С видит файл через НайтиФайлы, но при попытке чтения получит ошибку совместного доступа либо загрузит поврежденные данные.
Для гарантированной проверки того, что копирование завершено и файл свободен, мы можем использовать попытку монопольного доступа к нему. Самый простой и надежный способ в операционной системе Windows — попробовать переименовать или переместить файл сам в себя. Напишем вспомогательную функцию:
Функция ФайлЗанятДругимПроцессом(ПолноеИмяФайла)
Попытка
// Пытаемся переместить файл в то же самое место с тем же именем.
// Если файл занят записью сторонней утилиты, ОС выдаст ошибку.
ПереместитьФайл(ПолноеИмяФайла, ПолноеИмяФайла);
Возврат Ложь; // Файл свободен, можно обрабатывать
Исключение
Возврат Истина; // Файл заблокирован или еще записывается
КонецПопытки;
EndФункции
Также хорошей практикой является использование файлов-маркеров. Если у вас есть возможность повлиять на разработчиков отправляющей стороны, попросите их выгружать файлы в два шага:
data.xls.data.ready.В коде 1С вы настраиваете НайтиФайлы на поиск файла *.ready. Если он найден — вы гарантированно можете читать соответствующий файл данных.
В сообщениях форума активно обсуждается вопрос своевременного информирования о проблемах. Если мы просто будем писать лог, администратор узнает о проблеме только при проверке. Нам необходимо настроить проактивное уведомление.
Рассмотрим две стратегии уведомлений:
ИсторияЗагрузкиФайлов для текущего дня все еще отсутствует — это повод отправить экстренное сообщение в Telegram или на Почту.Разберем пример реализации отправки уведомления в Telegram-канал при нарушении SLA:
Процедура ПроверитьНарушениеSLA() Экспорт
ТекущееВремя = ТекущаяДата();
КритическоеВремя = НачалоДня(ТекущееВремя) + 7 * 3600; // 07:00 утра
Если ТекущееВремя >= КритическоеВремя Тогда
Если Не ИмпортЗаСегодняВыполнен(НачалоДня(ТекущееВремя)) Тогда
ОтправитьУведомлениеВТелеграм("Внимание! По состоянию на 07:00 файл импорта не был обнаружен и обработан!");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ОтправитьУведомлениеВТелеграм(ТекстСообщения)
ТокенБота = "123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ";
ИдентификаторЧата = "-100123456789";
Соединение = Новый HTTPСоединение("api.telegram.org", 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос("/bot" + ТокенБота + "/sendMessage");
Запрос.УстановитьТелоИзСтроки("chat_id=" + ИдентификаторЧата + "&text=" + КодироватьСтроку(ТекстСообщения, СпособКодированияСтроки.КодировкаURL));
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Исключение
ЗаписатьДанныеВЛог("Не удалось отправить сообщение в Telegram: " + ОписаниеОшибки(), Истина);
КонецПопытки;
КонецПроцедуры
Для надежной интеграции через файловый обмен всегда придерживайтесь следующих правил: