Стандартное API для чтения файлов не смогло прочитать файлы, которые само же и записало. Без ошибок, без исключений, без единого сообщения в журнале. Просто тихо вернуло пустоту.
Эта история — про библиотеку стандартных подсистем, кастомизацию, которая незаметно ломает контракты, и про то, почему «silent failure» — самый дорогой тип ошибок.
Задача
Две доработки в одном проекте. Первая — печать расходных накладных с фотографиями документов качества. Вторая — выгрузка каталога товаров на сайт с картинками. Обе задачи требуют одного: получить двоичные данные присоединённых файлов из базы 1С.
Файловая подсистема — из библиотеки стандартных подсистем (БСП), встроенной в конфигурацию. Стандартный механизм: справочник «ПрисоединённыеФайлы», регистр сведений для хранения, программный интерфейс для чтения и записи.
Первый подход: напрямую
Исходный код предыдущего разработчика читал ресурс «Хранилище» из регистра сведений напрямую запросом. Файлы не приходили — ресурс пуст. Стандартная рекомендация от 1С: не лезь в регистр руками, используй программный интерфейс.
Второй подход: по правилам
Метод ПолучитьДвоичныеДанныеФайла() из модуля БСП — одна строка, документированная, предсказуемая. Передаёшь ссылку на файл, получаешь двоичные данные. Всё просто.
Заменил прямой запрос на вызов API. Запустил. Файлов нет.
API не падает с ошибкой. Не выбрасывает исключение. Просто возвращает Неопределено. Молча. Тихо. Как будто файла не существует.
Но файл существует. Открывается из карточки номенклатуры. Просматривается, печатается, скачивается. БСП его записала. БСП его показывает. Но БСП API говорит: «нет данных».
Раскопки: кастомный справочник
Начал копать в код БСП. Метод ПолучитьДвоичныеДанныеФайла() внутри делает вот что:
- Получает ссылку на файл
- Читает реквизиты:
ТипХраненияФайла,Том,ПутьКФайлу - По типу хранения решает, где искать данные: в томе на диске, в регистре сведений, или в облаке
- Читает и возвращает
Проблема на шаге 2. Справочник «ПрисоединённыеФайлы» в этой конфигурации — кастомный. Предыдущий разработчик создал его вручную, скопировав часть структуры из типовой. Но без реквизитов ТипХраненияФайла, Том, ПутьКФайлу.
Эти реквизиты — часть контракта. БСП API использует их, чтобы понять где лежит файл. Без них метод не знает, куда смотреть. Не ошибка — нет данных для принятия решения. Возврат Неопределено.
Но файлы же записались
Вот что меня озадачило. Файлы есть — я их вижу в интерфейсе. БСП их записала. БСП их показывает. Как?
Ответ нашёлся в регистре сведений «ПрисоединённыеФайлы». У него два ресурса с похожими именами:
- Хранилище — пустое. Legacy-ресурс, который остался от старой версии
- ХранимыйФайл — сюда БСП фактически складывает сжатые двоичные данные, когда не может определить тип хранения
Это fallback-механизм. БСП видит: реквизитов тома нет, тип хранения не определён. Вместо ошибки — пишет данные в ресурс «ХранимыйФайл» регистра. Молча. Работает. Показывает файлы в интерфейсе, потому что интерфейсная часть знает про этот fallback.
А вот API для программного чтения — не знает. Он ожидает реквизиты. Их нет. Возврат пустоты.
Решение
Прямой запрос к регистру за правильным ресурсом. Читаем «ХранимыйФайл» вместо «Хранилище». Результат — ХранилищеЗначения, из которого получаем двоичные данные через .Получить().
Три места в коде: печать накладных, выгрузка основной картинки товара, выгрузка дополнительных фотографий. Одна и та же проблема — одно решение.
Да, это обход стандартного API. Да, при обновлении БСП это может сломаться. Но альтернатива — добавить недостающие реквизиты в кастомный справочник, мигрировать данные, протестировать все сценарии. На рабочей базе с реальными пользователями. Прагматичный выбор очевиден. Если конфигурация живёт в Git и деплоится через CI/CD — такие изменения структуры становятся значительно управляемее: о настройке такого пайплайна — гайд по CI/CD для 1С на реальных граблях.
Почему это больно
Не из-за сложности. Из-за тишины.
API не сказал «реквизит не найден». Не сказал «тип хранения не определён». Не сказал «использую fallback». Просто вернул Неопределено. И я потратил несколько часов, прежде чем понял, что проблема не в моём коде, не в данных и не в правах доступа. Проблема в том, что кастомизация нарушила контракт, а API не умеет об этом сообщать.
Silent failure — самый дорогой тип ошибки. Ты не знаешь, что что-то не работает, пока не начнёшь целенаправленно искать. А если ищешь не в ту сторону — копаешь яму, вместо того чтобы обойти забор. Похожий принцип работает с транзакциями: код выглядит правильно, данные записались наполовину — и ты узнаёшь об этом только через месяц. Про это — разбор бага с ACID и транзакциями в 1С.
Выводы для практики
- Если БСП API возвращает пустоту — проверьте, не кастомный ли справочник. Сравните реквизиты с типовой конфигурацией
- Два ресурса регистра «ПрисоединённыеФайлы» — «Хранилище» и «ХранимыйФайл» — это разные вещи. Если данные нет в первом, проверьте второй
- Fallback-механизмы БСП умеют записывать данные нестандартным путём, но стандартное API для чтения может об этом не знать
- Когда кастомизация расходится со стандартом, стандартные инструменты перестают работать. Ломаются молча — без ошибок, без логов, без подсказок
В системах с неявными контрактами самый коварный баг — тот, который не выглядит как баг. Выглядит как отсутствие данных. Такие же неявные ловушки прячутся в трёх слоях настроек СКД — когда отчёт у разработчика показывает шесть колонок, а у бухгалтера два.


