Как извлечь файлы из базы MS SQL 1С внешними средствами

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия IT и автоматизация бизнеса
← На главную

При работе с базами данных 1С:Предприятие на уровне SQL Server часто возникает задача получить доступ к прикрепленным файлам (сканам договоров, PDF-документам, изображениям) напрямую, минуя интерфейс системы. Однако простое чтение поля типа image или varbinary(max) не дает желаемого результата: файлы не открываются стандартными средствами. Это происходит потому, что 1С использует внутренний контейнер под названием ХранилищеЗначения. В данной статье мы подробно разберем, как устроено это хранилище и как программно извлечь из него «чистые» данные.

Анализируем структуру данных ХранилищаЗначения

Для начала разберем, почему обычный экспорт бинарных данных из SQL не работает. 1С не просто записывает байты файла в таблицу, она оборачивает их в специализированную структуру. Если мы посмотрим на начало бинарного потока любого файла в базе 1С, мы увидим характерную последовательность байтов. Например: 0x0201534B6FF4888DC14EA0D5EBB6BDA0A70D.

Давайте проанализируем этот заголовок. Он состоит из 18 или 20 байт (в зависимости от версии платформы):

  1. Первые 2 байта (02 01): Сигнатура объекта 1С, указывающая, что перед нами именно объект типа ХранилищеЗначения.
  2. Следующие 16 байт: Уникальный идентификатор или системный маркер, который используется платформой для внутренних проверок целостности и метаданных.

Следовательно, первая задача при извлечении — отсечь этот заголовок. Все, что идет после первых 18-20 байт, является полезной нагрузкой, но и здесь нас ждет нюанс: данные практически всегда сжаты.

Разбираемся с алгоритмом сжатия

Платформа 1С:Предприятие для экономии места в базе данных использует стандартный алгоритм сжатия zlib (Deflate). Это означает, что после того как мы убрали заголовок 1С, перед нами оказывается «сырой» zlib-поток.

Рассмотрим пошаговый алгоритм обработки данных:

  1. Получаем бинарное содержимое поля из SQL-таблицы.
  2. Удаляем первые 18 (реже 20) байт заголовка.
  3. Проверяем оставшиеся данные на наличие zlib-заголовка. Обычно zlib-поток начинается с байтов 78 9C или 78 DA.
  4. Применяем функцию распаковки (например, gzuncompress в PHP или аналоги в Python/C#).

Реализация извлечения на языке PHP

Посмотрим на практический пример. Предположим, мы подключаемся к базе через PDO и пытаемся достать PDF-файл. Важно правильно обработать бинарный поток, чтобы драйвер не интерпретировал его как обычную строку.


// 1. Подготовка запроса к базе данных
$STH = $DB1C->prepare("SELECT _Fld8845 as data FROM _Reference8748 WHERE _IDRRef=:id");
$STH->bindParam(':id', $binaryId, PDO::PARAM_LOB);
$STH->execute();
$row = $STH->fetch();
$rawData = $row["data"];

// 2. Отсекаем заголовок 1С (18 байт)
$payload = substr($rawData, 18);

// 3. Распаковываем данные
// Используем gzuncompress, так как данные внутри ХранилищаЗначения обычно имеют zlib-заголовок
$unpackedData = @gzuncompress($payload);

if ($unpackedData === false) {
    // Если gzuncompress не сработал, пробуем gzinflate (для «чистого» deflate потока)
    $unpackedData = gzinflate(substr($payload, 2)); 
}

// 4. Отправляем файл в браузер
header("Content-Type: application/pdf");
echo $unpackedData;

Возможная дополнительная обертка (Сериализация)

В некоторых случаях после распаковки zlib мы можем обнаружить, что данные все еще не являются чистым PDF-файлом. Это происходит, если 1С поместила в ХранилищеЗначения не просто ДвоичныеДанные, а другой объект (например, структуру или массив с метаданными).

Проанализируем распакованный поток. Если он начинается с текста в фигурных скобках {"#", ...}, это означает, что внутри находится сериализованный объект 1С. Чтобы найти в нем сам файл, нужно:

Рассмотрим ситуацию, когда нам нужно найти начало файла по его сигнатуре:


$pdfStart = strpos($unpackedData, "%PDF-");
if ($pdfStart !== false) {
    $cleanPdf = substr($unpackedData, $pdfStart);
    // Теперь $cleanPdf содержит валидный PDF-документ
}

Альтернативные способы решения задачи

Если работа с прямыми SQL-запросами и бинарными заголовками кажется слишком трудоемкой, проанализируем другие варианты, предлагаемые сообществом:

  1. Использование Web-сервисов 1С: Мы можем написать внутри 1С небольшой Web-сервис, который будет принимать ID объекта, считывать ХранилищеЗначения штатными средствами 1С (Получить()) и возвращать Base64 строку. Это самый надежный способ, так как платформа сама берет на себя распаковку и обработку форматов.
  2. COM-соединение: Похожий метод, но требующий наличия установленной платформы на стороне клиента. Метод New COMObject("V83.COMConnector") позволяет подключиться к базе и вызвать экспорт функций.
  3. Хранение файлов в томах на диске: Это архитектурное решение. В настройках 1С можно включить хранение присоединенных файлов не в базе SQL, а в сетевых каталогах (томах). В этом случае в SQL-таблице будет храниться только путь к файлу, который можно легко прочитать любым внешним скриптом.

Резюме

Мы выяснили причину, по которой файлы из 1С не открываются «в лоб»: это наличие 18-байтового технического заголовка и сжатие zlib. Для успешного извлечения данных внешними средствами необходимо последовательно очистить бинарный поток от метаданных платформы и произвести декомпрессию. Помните, что прямая работа с таблицами SQL в обход платформы 1С формально не рекомендуется производителем, поэтому такие решения следует использовать с осторожностью, обеспечивая контроль целостности данных.

← На главную