При разработке распределенных информационных систем (РИБ) на платформе 1С часто возникает задача выноса файлов (картинок, документов) из базы данных в локальный каталог. Это позволяет существенно уменьшить размер самой базы и ускорить выполнение бэкапов. Однако, если система предполагает работу узлов в оффлайне (например, на ноутбуке в поездке), возникает серьезная архитектурная проблема: как синхронизировать эти внешние файлы между центральным и подчиненным узлами без использования облачных сервисов, которые часто создают дубли и конфликты.
Рассмотрим детально проверенные способы решения этой задачи, проанализируем их плюсы и минусы, и разберем по шагам наиболее надежный программный метод реализации средствами встроенного языка 1С.
Многие разработчики в первую очередь рассматривают использование облачных сервисов (Яндекс.Диск, Google Drive, OneDrive) для синхронизации папок. Но в контексте РИБ этот подход имеет критические недостатки:
Именно поэтому для надежной работы оффлайн-узлов необходимо встраивать логику передачи файлов непосредственно в механизм обмена 1С или использовать специализированные P2P-инструменты.
Проанализируем наиболее изящный метод, предложенный в обсуждении. Суть его заключается в том, что файлы перемещаются между узлами внутри стандартного XML-сообщения обмена РИБ, но не задерживаются в базе данных на постоянной основе. Рассмотрим алгоритм реализации по шагам.
1. Создание структуры хранения данных
В справочнике, который отвечает за хранение информации о файлах (например, Справочник.ПрисоединенныеФайлы), создадим служебный реквизит ДанныеДляПередачи с типом ХранилищеЗначения. Этот реквизит должен быть включен в состав плана обмена.
2. Обработка перед отправкой данных
В модуле объекта справочника или в подписке на событие ПередЗаписью (при условии обмена) реализуем логику: если файл новый или изменился, мы считываем его из локальной папки и помещаем в наш служебный реквизит.
// Пример помещения файла в транзитный реквизит
Если ЭтоНовый() Или ФайлБылИзменен Тогда
ПолныйПуть = ПолучитьПолныйПутьКФайлу(ЭтотОбъект.ПутьНаДиске);
ДвоичныеДанные = Новый ДвоичныеДанные(ПолныйПуть);
ЭтотОбъект.ДанныеДляПередачи = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
КонецЕсли;
3. Обработка при получении данных на другом узле
При загрузке сообщения РИБ на принимающей стороне сработает событие записи объекта. В этот момент нам нужно извлечь данные из реквизита ДанныеДляПередачи, сохранить их в локальное файловое хранилище узла и — что крайне важно — очистить реквизит ДанныеДляПередачи, чтобы он не раздувал базу данных.
// Пример извлечения файла на узле-приемнике
Процедура ПриЗаписи(Отказ)
Если ЭтотОбъект.ДанныеДляПередачи <> Неопределено Тогда
Данные = ЭтотОбъект.ДанныеДляПередачи.Получить();
Если ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда
ПутьСохранения = ОпределитьЛокальныйПуть(ЭтотОбъект);
Данные.Записать(ПутьСохранения);
// Очищаем, чтобы не занимать место в БД
ЭтотОбъект.ДанныеДляПередачи = Неопределено;
// Важно: отключаем обмен для этой записи, чтобы не зациклить
ЭтотОбъект.ОбменДанными.Загрузка = Истина;
ЭтотОбъект.Записать();
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот метод гарантирует, что файл "приедет" на другой узел строго вместе с метаданными (ссылкой, наименованием, автором) в одном пакете обмена. Сразу после "приезда" файл выгружается на диск, а база остается компактной.
Если объем файлов исчисляется гигабайтами, передача их через XML-сообщения РИБ может привести к ошибкам нехватки памяти (Out of Memory) или гигантским файлам обмена. В этом случае выясним, как настроить прямую синхронизацию папок минуя облако.
Рассмотрим инструмент Syncthing. Это бесплатное решение с открытым исходным кодом, которое идеально подходит для связки "Компьютер - Ноутбук":
rsync), что экономит трафик.Для реализации этого подхода в 1С достаточно хранить в базе относительные пути к файлам. Например, корень хранилища на ПК — C:\Work\Files\, на ноутбуке — D:\Storage\Files\. В базе храним только my_doc.pdf. Перед открытием файла программно подставляем путь к локальному корню, используя константу или параметр сеанса.
Если мы выбрали Решение №1 (передача через базу), проанализируем ситуацию с объемом данных. Стандартная запись ХранилищеЗначения в XML преобразует двоичные данные в формат Base64, что увеличивает размер сообщения на 33%. Для оптимизации рекомендуем:
ЗаписьXML и ЧтениеXML напрямую для обработки данных порциями, если вы пишете свой механизм обмена.РегистрСведений.ОчередьПередачиФайлов. Это позволит более гибко управлять очередью: например, не отправлять файлы тяжелее 10 Мб, если связь нестабильна, или отправлять их отдельным пакетом обмена.Это самый тонкий момент архитектуры. Если мы просто удалим файл на диске, другой узел может этого "не заметить". Проанализируем правильный алгоритм удаления:
ФайлУдален = Истина.ПриЗаписи), которое проверяет этот флаг и физически удаляет файл с локального диска.Таким образом, мы исключаем ситуацию "воскрешения" файлов, которая часто встречается при использовании обычных файловых синхронизаторов.
Выбор решения зависит от масштаба системы. Для небольшого количества документов идеально подходит метод транзитного реквизита (Решение №1). Он прост в реализации, не требует стороннего ПО и гарантирует целостность данных. Если же база предполагает работу с тяжелым контентом (фото высокого разрешения, видео), стоит посмотреть в сторону P2P-синхронизации (Решение №2), оставив за 1С только управление логикой прав и путей.