2015-01-29 4 views
3

Я пытаюсь заархивировать файл с помощью модуля python gzip, а затем hash gzipped filed using hashlib. У меня есть следующий код:Python md5 хэши одного и того же файла gzipped несовместимы

import hashlib 
import gzip 

f_name = 'read_x.fastq' 

for x in range(0,3): 

    file = open(f_name, 'rb') 

    myzip = gzip.open('test.gz', 'wb', compresslevel=1) 

    n = 100000000 
    try: 
     print 'zipping ' + str(x) 
     for chunk in iter(lambda: file.read(n), ''): 
      myzip.write(chunk) 
    finally: 
     file.close() 
     myzip.close() 

    md5 = hashlib.md5() 
    print 'hashing ' + str(x) 
    with open('test.gz', 'r') as f: 
     for chunk in iter(lambda: f.read(n), ''): 
      md5.update(chunk) 

    print md5.hexdigest() 
    print '\n' 

, который я думал, что просто должен пронестись файл, хэш его и отображает тот же вывод, окрошка три раза подряд. Тем не менее, выход я получаю:

zipping 0 
hashing 0 
7bd80798bce074c65928e0cf9d66cae4 


zipping 1 
hashing 1 
a3bd4e126e0a156c5d86df75baffc294 


zipping 2 
hashing 2 
85812a39f388c388cb25a35c4fac87bf 

Если я уйду из шаг GZIP, и только хэш один и тот же сжатыми файлами три раза подряд, я действительно получить тот же выход три раза:

hashing 0 
ccfddd10c8fd1140db0b218124e7e9d3 


hashing 1 
ccfddd10c8fd1140db0b218124e7e9d3 


hashing 2 
ccfddd10c8fd1140db0b218124e7e9d3 

Может ли кто-нибудь объяснить, что здесь происходит? Проблема должна заключаться в том, что процесс gzip отличается каждый раз. Но, насколько я знал, алгоритмом DEFLATE является кодировка Хаффмана, за которой следует LZ77 (форма кодирования длины пробега) или LZ77, за которым следует Хаффман, и поэтому заданный идентичный ввод должен давать идентичный результат.

ответ

2

Есть несколько причин, почему сжимающие точно такое же содержание будет производить различные выходы GZIP:

  • степень сжатия. Это можно контролировать с помощью параметра уровня сжатия.
  • Имя исходного файла, находящегося в заголовке. Это можно контролировать, если вы используете gzip.GzipFile api, а не gzip.open api.
  • Время модификации, которое также находится в заголовке, а также может управляться с помощью gzip.GzipFile api.

Так вот кусок кода, который продемонстрировал неправильный и правильный способ получить воспроизводимый выход из питона Gzip:

import hashlib 
import gzip 

f_name = '/etc/passwd' 
output_template = '/tmp/test{}.gz' 

def digest(filename: str) -> str: 
    md5 = hashlib.md5() 
    with open(output_filename, 'rb') as f: 
     for chunk in iter(lambda: f.read(block_size), b''): 
      md5.update(chunk) 
    return md5.hexdigest() 

print("The default way - non identical outputs") 
for x in range(0,3): 
    input_handle = open(f_name, 'rb') 
    output_filename = output_template.format(x) 
    myzip = gzip.open(output_filename, 'wb') 
    block_size = 4096 
    try: 
     for chunk in iter(lambda: input_handle.read(block_size), b''): 
      myzip.write(chunk) 
    finally: 
     input_handle.close() 
     myzip.close() 
    print(digest(output_filename)) 

print("The right way to get identical outputs") 
for x in range(3,6): 
    input_handle = open(f_name, 'rb') 
    output_filename = output_template.format(x) 
    myzip = gzip.GzipFile(
     filename='', # do not emit filename into the output gzip file 
     mode='wb', 
     fileobj=open(output_filename, 'wb'), 
     mtime=0, 
    ) 
    block_size = 4096 
    try: 
     for chunk in iter(lambda: input_handle.read(block_size), b''): 
      myzip.write(chunk) 
    finally: 
     input_handle.close() 
     myzip.close() 
    print(digest(output_filename)) 
2

О, но подождите .... очевидно, gzip добавляет информацию о метке времени в заголовок файла gzip, поэтому хеш будет отличаться.

+1

Correct. Более того, различные уровни сжатия, разные версии кода сжатия и в зависимости от кода, возможно, разные компиляции одного и того же кода могут давать разные результаты. Ничего из этого не имеет значения. Важно то, что _decompression_ данных дает вам именно то, что было сжато. Если вы собираетесь вычислить подпись чего-то, это должны быть входные данные, а не сжатые данные. –

+1

Ну, мне нужно gzip файлы для загрузки на репо. Для репо требуется md5 файла gzipped, чтобы проверить отсутствие ошибок при передаче. Таким образом, подпись должна быть создана из сжатых данных. Но спасибо за ваш вклад. – shaw2thefloor

+0

Это нормально для одного выстрела. Я положил подпись md5 моих дистрибутивов .tar.gz на веб-сайт для проверки. Просто не ожидайте, что кто-то другой, сжимающий одни и те же данные, получит одну и ту же подпись. –

Смежные вопросы