Сжатие/Распаковка данных по алгоритму Deflate встроенными (!) средствами платформы 1С

Сжатие/Распаковка данных по алгоритму Deflate встроенными (и только!) средствами платформы 1С. Теперь работать напрямую с данными в СУБД стало проще ))

Intro

Алгоритм компресии Deflate является старым, но проверенным временем форматом для сжатия данных без потерь. Яркий пример его применения — это файловые архивы ZIP. Deflate используется во многих программах. В том числе и в платформе 1С для хранения информации в более компактном представлении. Иногда приходится манипулировать данными напрямую через СУБД и тут нужен какой-то инструмент для распаковки сжатых данных. Хотя платформа 1с делает это на лету и прозрачно для пользователя.

Что под капотом

Распаковка данных, сжатых по алгоритму Deflate, осуществляется следующим образом: к сжатым данным клеятся структуры описания формата ZIP и на выходе получается почти корректный ZIP файл. Далее натравляем на него класс чтения ZIP файла и извлекаем, пусть и с выбросом ошибки, но полностью правильный, исходный файл.
Сжатие данных идет в обратном порядке: на файл натравливаем класс записи ZIP файла и пакуем этот файл, далее, отбрасываем структуры, описывающие ZIP формат, и на выходе получаем голые данные, сжатые по алгоритму Deflate.
Непосредственно, работа с Deflate здесь не реализована, выручают классы по работе с ZIP архивами. Но зато для сжатия/распаковки не требуются внешние компоненты и/или COM объекты.
Все это стало удобным с версии платформы 8.3.9+, когда появились классы для удобной работы с бинарными данными. Можно легко манипулировать отдельными байтами.

Что мы получаем

Здесь представлена обработка, позволяющая производить сжатие отдельного файла по алгоритму Deflate, а также распаковывать из сжатого файла данные. Также в коде представлены методы для преобразования чисел между любыми системами счисления (от 2 до 36). Основной движок обработки можно легко перенести в вашу БД/обработку. Механизм может независимо работать или на клиенте, или на сервере, причем в любой поддерживаемой ОС.

FAQ

Q: В коде много магических чисел, много лишних операций, зачем так неоптимально?
A: Это сделано для наглядности, чтобы расписать более подробно формат ZIP.

Q: А если обработка сжимает/распаковывает не правильно или по своему алгоритму? Как это проверить?
A: Легко. Создаете реквизит с типом ХранилищеЗначения, засовываете в него данные, указываете уровень сжатия > 0. В вашей СУБД дергаете из соответствующего поля двоичное значение, отбрасываете первые 18 байт (это эска пишет описание для ХЗ), и в остатке будет голый Deflate.

Q: Где это используется?
A: Это используется в продакшене при выносе большого объема бинарных данных из базы эски во внешнюю БД, из которой потом при запросе подтягиваются данные через внешние источники, которые разжимаются данным механизмом.

P.S.

Как оказалось, на ИС уже есть похожие публикации, но там либо применяется внешний EXE-шник, либо внешняя компонента, либо работа с бинарными данными идет через европу.

Update 25.01.2024 (v.1.1)

Исправлена ошибка в механизме сжатия (в 4-х байтовый буфер считывалось 2 байта о размере сжатых данных). Исправлены незначительные ошибки. Скорректирован интерфейс. Требуется платформа 8.3.9+ и включенный режим использования синхронных вызовов расширений платформы и внешних компонент.

34 Comments

  1. SerVer1C

    (1) Да, действительно, таким образом можно распаковать значение из бинарника, полученного из скульного значения 1с-ного ХЗ. Значит, приведенный в статье алгоритм останется для каких-то специфических задач при распаковке данных, сжатых алгоритмом Deflate каким-либо альтернативным ПО.

    Reply
  2. lucas_

    В развитие темы — а нельзя ли распаковать BinaryData, используя DECOMPRESS (функцию sql 2016)? Не подскажите, нет ли где описания формата запакованного бинарника 1С?

    Reply
  3. SerVer1C

    (3) Судя по описанию функции DECOMPRESS, она как раз может извлечь данные из сжатого 1С-кой хранилища значений. Только придется отбросить первые 18 байт — это описание для ХЗ. Точное описание формата данного заголовка не встречал.

    Reply
  4. lucas_

    Нет, не получается.

    Declare @b varbinary(max) =
    (SELECT BinaryData FROM     Params WHERE  (FileName = N’DBNames’))
    Set @b =  SUBSTRING(@b, 19, LEN(@b)-18)
    Select  DECOMPRESS(@b)
    

    Выдает: Сообщение 9826, уровень 16, состояние 1, строка 4

    В качестве аргумента во встроенную инструкцию DECOMPRESS переданы несжатые или поврежденные данные.

    Reply
  5. AlkB

    Данный метод действительно работает. А метод с СериализаторXDTO у меня не заработал.

    Reply
  6. SerVer1C

    (5) Дело вот в чем: когда мы сжимаем данные функцией COMPRESS, то получается на выходе бинарник следующей структуры: 10 байт описание GZIP формата (здесь всегда 0x1F8B0800000000000400), далее идет Deflate-поток, затем 4 байта CRC несжатых данных и в конце 4 байта — размер несжатых данных. При выполнении DECOMPRESS функция сверяет CRC и размер исходных данных из сжатого бинарника с рассчитанным CRC и размером распакованных данных. В случае их расхождения получается та самая ошибка. Из поля скуля, содержащего эсовское ХЗ, мы можем получить все, кроме CRC исходных данных. Поэтому чисто средствами скуля распаковать эсовское хранилище не получится.

    Deflate-поток легко распаковывается приведенной в статье обработкой.

    Также если собрать вручную GZIP на основе Deflate-потока из эсовского ХЗ и подставить корректный CRC и размер исходных данных, то DECOMPRESS распакует исходные данные.

    Если кто знает, как в ф. DECOMPRESS игнорить CRC, то напишите 🙂

    Reply
  7. lucas_

    Ну, далее — просто мысли вслух. Я не совсем уловил. 1С-бинарник где-то внутри себя содержит CRC? Это CRC такое же, как и у аналогичных данных, сжатых COMPRESS или же другое?

    Reply
  8. SerVer1C

    (8) В том-то и печаль, что 1С-бинарник НЕ содержит внутри себя CRC. А ф. DECOMPRESS обязательно требует CRC исходных данных для распаковки.

    Reply
  9. lucas_

    Жаль. Есть еще некая функция DECOMPRESSSCALAR. Но не найти ее описания, не заставить ее работать мне пока не удалось.

    Reply
  10. SerVer1C

    (10) Как я понял, это такой баг, когда IntelliSence отображает внутренние функции. И исправлять этот баг пока не собираются. Пруф: https [] connect.microsoft.com/SQLServer/feedback/details/688606/denali-ctp3-internal-system-fuctions-are-exposed-in-intellisence-hints

    Reply
  11. lucas_

    То есть работать на решение поставленной задачи данная функция точно не будет?

    Reply
  12. SerVer1C

    (12) DECOMPRESSSCALAR — не будет. DECOMPRESS будет, если в нее передавать правильный GZIP. DECOMPRESS даже распакует архив, созданный в 7-zip с форматом GZIP и методом сжатия Deflate.

    Reply
  13. lucas_

    Опять же мысли вслух — CRC есть функция сжатого бинарника? Возможно, ее имеет смысл просто вычислить средствами скуля? Вот здесь http://www.zlib.org/rfc-gzip.html#crc-code, на мой первый непросвещенный взгляд, все выглядит не очень страшно.

    Reply
  14. SerVer1C

    (14) CRC считается 25-ю строками кода — это не проблема. НО только CRC надо рассчитывать не от сжатых данных, а от исходных! А пока мы сжатые данные не распакуем, то не посчитаем CRC исходных данных. Вот такая вот рекурсия ))

    Reply
  15. Alligator84

    Есть ли возможность DECOMPRESS DT?

    Reply
  16. SerVer1C

    (16) Нет. Файл DT имеет сложную структуру. Вдоль и поперек данный формат исследовал пользователь awa. Почитайте его статьи.

    Reply
  17. user894822

    (1) Александр, а если на входе — двоичные данные, сжатые алгоритмом deflate, а на выходе нужно получить распакованные данные. Существует ли где-то описание заголовка, который нужно добавить к входным данным, чтобы СериализаторXDTO воспринимал их как корректный тип ХранилищеЗначения, содержащий двоичные данные?

    Reply
  18. user894822

    Попробовал скормить обработке свой сжатый файл — не работает, на выходе создаёт файл нулевой длины. При этом тот же файл корректно распаковывается онлайновым распаковщиком http://txtwizard.net/compression.

    Платформа 8.3.10.2466.

    Reply
  19. alexandersh

    (del)

    Reply
  20. alexandersh

    (18)

    (1) Александр, а если на входе — двоичные данные, сжатые алгоритмом deflate, а на выходе нужно получить распакованные данные. Существует ли где-то описание заголовка, который нужно добавить к входным данным, чтобы СериализаторXDTO воспринимал их как корректный тип ХранилищеЗначения, содержащий двоичные данные?

    так а тут как раз по теме самой статьи, добиваешь описанием структуры до zip-архива и распаковывашь)

    Reply
  21. user894822

    (21) Я имел в виду по вашему методу, через хранилище значения, а не через zip-файлы. Но потом посмотрел, как выглядит сериализация несжатого ХЗ и понял, что так не получится. Там двоичные данные заворачиваются в более сложную структуру, а потом это всё сжимается.

    А авторская обработка у меня не заработала на моём примере данных. То, что сжато обработкой, распаковывает ок. А данные, сжатые сторонней программой — нет. Хотя данные корректные, проверял независимым распаковщиком.

    Reply
  22. SerVer1C

    (22) Прошу предоставить пример любого бинарника, сжатого deflate. Распаковка должна отрабатывать правильно, если это чистый deflate поток, т. к. на данном алгоритме работает распаковщик из публикации 710201 , и мной не было найдено EFD файла, который невозможно было бы распаковать.

    Reply
  23. SerVer1C

    (22)

    Хотя данные корректные, проверял независимым распаковщиком

    Предлагаю вам проверить с помощью утилиты V8Unpack. С ключом «-D» сжимаете данные, распаковываете сжатый файл моей обработкой. Затем моей обработкой сжимаете данные и распаковывайте сжатый файл утилитой с ключом «-I».

    Reply
  24. user894822

    (23) Разобрался, оказалось у меня были данные в формате zlib (rfc-1950), там 2 байта дополнительный заголовок.

    Reply
  25. markers

    Вдруг кому пригодится, результат использования данной публикации.

    Автор, спасибо!

    Reply
  26. user792176

    (7) в регистре сведений ВерсииОбъектов есть реквизит КонтрольнаяCумма, не там ли хранится искомый CRC ?

    Reply
  27. SerVer1C

    (27)

    ВерсииОбъектов

    В моей конфе нет такого регистра сведений.

    Reply
  28. user792176

    (28) насколько понимаю, во всех новых конфигурациях у которых есть версионирование, есть этот регистр, в него пишется история изменений объектов.

    Я этим вопросом заинтересовался в контексте извлечения истории изменения объектов средствами sql, попробовал Ваш метод, не получается…. у меня конфигурация ЕРП 2.

    если поможете разобраться, был-бы благодарен..

    Reply
  29. SerVer1C

    (29)

    благодарен

    В РС «ВерсииОбъектов» реквизит «КонтрольнаяСумма» — это простой md5 хэш. (см. ОМ «ВерсионированиеОбъектов» функция «КонтрольнаяСумма»). Что конкретно вы хотите сделать через SQL ?

    Reply
  30. user792176

    (30) хочу получить строку с данными версии объекта, я так понимаю туда пишется сериализованные данные объекта, вот их и хотелось бы получить.

    вот еще один путь как расшифровать, хотелосьбы чтото типа такого, но без использования sys.sp_OACreate

    https://www.sql.ru/forum/1051208/chtenie-sredstvami-sql-iz-1s-8-1-polya-tipa-hranilishheznacheniya-ne-risunok

    Reply
  31. user792176

    (30) хотелбы получить строковое представление сериализованной версии данных объекта (в хранилище ведь оно пишется)

    но без использования sys.sp_OACreate, как есть предложение на sql ru

    sql.ru

    Reply
  32. SerVer1C

    (31) Так данная обработка распакует 1с-ное ХЗ. См. FAQ из статьи: Создаете реквизит с типом ХранилищеЗначения, засовываете в него данные, указываете уровень сжатия > 0. В вашей СУБД дергаете из соответствующего поля двоичное значение, отбрасываете первые 18 байт (это эска пишет описание для ХЗ), и в остатке будет голый Deflate.

    Reply
  33. SlavaKron

    (7) Судя по всему, она таки игнорирует CRC, потому что, например следующий запрос к любой базе 1С возвращает распакованные данные в кодировке UTF-8:

    SEL ECT
    DECOMPRESS(0x1F8B0800000000000400 + BinaryData) as DBNames
    
    FR OM Params
    
    WH ERE
    FileName = ‘DBNames’

    Соответственно, для ХЗ:

    SELECT
    DECOMPRESS(0x1F8B0800000000000400 + SUBSTRING(_Fld26, 19, LEN(_Fld26) — 18)) as Data
    FR OM
    _Reference18
    Reply
  34. SerVer1C

    (34) ДА! Отличное решение. Это работает! Протестировано на 2016-м скуле. CRC не проверяется! Видимо, запутался в байтах, когда проверял в прошлый раз.

    Чтобы получить чистые данные сразу из эсовского ХЗ, используем:

    SELECT

    SUBSTRING(DECOMPRESS(0x1F8B0800000000000400 + SUBSTRING(Field, 19, 9223372036854775807)), 9, 9223372036854775807)

    FROM

    Table

    P.S. 9223372036854775807 — это максимальное положительное значение bigint

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *