При работе с базами данных 1С:Предприятие на уровне SQL Server часто возникает задача получить доступ к прикрепленным файлам (сканам договоров, PDF-документам, изображениям) напрямую, минуя интерфейс системы. Однако простое чтение поля типа image или varbinary(max) не дает желаемого результата: файлы не открываются стандартными средствами. Это происходит потому, что 1С использует внутренний контейнер под названием ХранилищеЗначения. В данной статье мы подробно разберем, как устроено это хранилище и как программно извлечь из него «чистые» данные.
Для начала разберем, почему обычный экспорт бинарных данных из SQL не работает. 1С не просто записывает байты файла в таблицу, она оборачивает их в специализированную структуру. Если мы посмотрим на начало бинарного потока любого файла в базе 1С, мы увидим характерную последовательность байтов. Например: 0x0201534B6FF4888DC14EA0D5EBB6BDA0A70D.
Давайте проанализируем этот заголовок. Он состоит из 18 или 20 байт (в зависимости от версии платформы):
ХранилищеЗначения.Следовательно, первая задача при извлечении — отсечь этот заголовок. Все, что идет после первых 18-20 байт, является полезной нагрузкой, но и здесь нас ждет нюанс: данные практически всегда сжаты.
Платформа 1С:Предприятие для экономии места в базе данных использует стандартный алгоритм сжатия zlib (Deflate). Это означает, что после того как мы убрали заголовок 1С, перед нами оказывается «сырой» zlib-поток.
Рассмотрим пошаговый алгоритм обработки данных:
78 9C или 78 DA.gzuncompress в PHP или аналоги в Python/C#).Посмотрим на практический пример. Предположим, мы подключаемся к базе через 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С. Чтобы найти в нем сам файл, нужно:
%PDF-.Рассмотрим ситуацию, когда нам нужно найти начало файла по его сигнатуре:
$pdfStart = strpos($unpackedData, "%PDF-");
if ($pdfStart !== false) {
$cleanPdf = substr($unpackedData, $pdfStart);
// Теперь $cleanPdf содержит валидный PDF-документ
}
Если работа с прямыми SQL-запросами и бинарными заголовками кажется слишком трудоемкой, проанализируем другие варианты, предлагаемые сообществом:
ХранилищеЗначения штатными средствами 1С (Получить()) и возвращать Base64 строку. Это самый надежный способ, так как платформа сама берет на себя распаковку и обработку форматов.New COMObject("V83.COMConnector") позволяет подключиться к базе и вызвать экспорт функций.Мы выяснили причину, по которой файлы из 1С не открываются «в лоб»: это наличие 18-байтового технического заголовка и сжатие zlib. Для успешного извлечения данных внешними средствами необходимо последовательно очистить бинарный поток от метаданных платформы и произвести декомпрессию. Помните, что прямая работа с таблицами SQL в обход платформы 1С формально не рекомендуется производителем, поэтому такие решения следует использовать с осторожностью, обеспечивая контроль целостности данных.