2010-09-23 1 views
4

Я хочу извлечь файл из zip на определенный путь, игнорируя путь к файлу в архиве. Это очень простой в Python 2.6 (моя строка документации длиннее, чем код)Как имитировать ZipFile.open в Python 2.5?

import shutil 
import zipfile 

def extract_from_zip(name, dest_path, zip_file): 
    """Similar to zipfile.ZipFile.extract but extracts the file given by name 
    from the zip_file (instance of zipfile.ZipFile) to the given dest_path 
    *ignoring* the filename path given in the archive completely 
    instead of preserving it as extract does. 
    """ 
    dest_file = open(dest_path, 'wb') 
    archived_file = zip_file.open(name) 
    shutil.copyfileobj(archived_file, dest_file) 


extract_from_zip('path/to/file.dat', 'output.txt', zipfile.ZipFile('test.zip', 'r')) 

Но в Python 2.5, метод ZipFile.open не доступен. Я не мог найти решение в stackoverflow, но у this forum post было хорошее решение, которое использует ZipInfo.file_offset для поиска нужной точки в zip и используйте zlib.decompressobj, чтобы распаковать байты оттуда. К сожалению, ZipInfo.file_offset был удален в Python 2.5!

Итак, учитывая, что все, что у нас есть в Python 2.5, это ZipInfo.header_offset, я подумал, что мне просто нужно разобрать и пропустить структуру заголовка, чтобы получить сам файл. Использование Wikipedia как a reference (я знаю) Я придумал это намного дольше и не очень изящное решение.

import zipfile 
import zlib 

def extract_from_zip(name, dest_path, zip_file): 
    """Python 2.5 version :(""" 
    dest_file = open(dest_path, 'wb') 
    info = zip_file.getinfo(name) 
    if info.compress_type == zipfile.ZIP_STORED: 
     decoder = None 
    elif info.compress_type == zipfile.ZIP_DEFLATED: 
     decoder = zlib.decompressobj(-zlib.MAX_WBITS) 
    else: 
     raise zipfile.BadZipFile("Unrecognized compression method") 

    # Seek over the fixed size fields to the "file name length" field in 
    # the file header (26 bytes). Unpack this and the "extra field length" 
    # field ourselves as info.extra doesn't seem to be the correct length. 
    zip_file.fp.seek(info.header_offset + 26) 
    file_name_len, extra_len = struct.unpack("<HH", zip_file.fp.read(4)) 
    zip_file.fp.seek(info.header_offset + 30 + file_name_len + extra_len) 

    bytes_to_read = info.compress_size 

    while True: 
     buff = zip_file.fp.read(min(bytes_to_read, 102400)) 
     if not buff: 
      break 
     bytes_to_read -= len(buff) 
     if decoder: 
      buff = decoder.decompress(buff) 
     dest_file.write(buff) 

    if decoder: 
     dest_file.write(decoder.decompress('Z')) 
     dest_file.write(decoder.flush()) 

Обратите внимание, как я распаковать и прочитать поле, которое дает длину дополнительного поля, так как вызов len на атрибут ZipInfo.extra дает 4 байта меньше, что вызывает смещение неправильно вычислялся. Может, я что-то упустил?

Можно ли улучшить это решение для Python 2.5?

Edit: Я должен был сказать, очевидное решение, как было предложено ChrisAdams

dest_file.write(zip_file.read(name)) 

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

Кроме того, обновление Python - это очевидное решение, но это полностью из моих рук и по существу невозможно.

+0

Почему вы не можете обновить? 2,7 является последним в t он 2.x, вы совсем устарели ... нет веской причины оставаться на старой версии – Daenyth

+2

@ Даенит Я только хочу. Попробуйте сообщить об этом операционной команде, которая управляет серверами x00 ... – Day

ответ

0

Учитывая мои ограничения, похоже, был дан ответ на мой вопрос: разобрать структуру ZipFile себя и использовать zlib.decompressobj чтобы разархивировать байты, как только вы их нашли.

Если у вас нет (/ страдают) мои ограничения, вы можете найти лучшие ответы здесь:

  1. Если вы можете, просто обновить версию 2.5 до 2.6, как это было предложено в (или позже!) комментарий Даенита.
  2. Если у вас есть только небольшие файлы в почтовом индексе, который может быть 100%, загруженным в памяти, используйте ChrisAdams' answer
  3. Если вы можете ввести зависимость от внешней утилиты, сделать соответствующий системный вызов /usr/bin/unzip или подобными, как это было предложено в Vlad's answer
4

не проверял это немного, но я использую что-то очень похожее в Python 2.4

import zipfile 

def extract_from_zip(name, dest_path, zip_file): 
    dest_file = open(dest_path, 'wb') 
    dest_file.write(zip_file.read(name)) 
    dest_file.close() 

extract_from_zip('path/to/file/in/archive.dat', 
     'output.txt', 
     zipfile.ZipFile('test.zip', 'r')) 
+0

'zip_file.read (name)' будет терпеть неудачу с MemoryError для любого файла с разумным размером, содержащегося в zip, потому что он пытается разложить все это на память в одном идти. Мне нужно выпустить его. Должен был упомянуть об этом в вопросе, но спасибо за предложение. – Day

1

Я знаю, что я немного опоздал на вечеринку по этому вопросу, но имел ту же проблему.

Решение, которое я использовал, было скопировать python 2.6.6 версия ZipFile и положить в папку (я назвал его python_fix) и импорта, что вместо того, чтобы:

python_fix/zipfile.py 

Затем в коде:

import python_fix.zipfile as zipfile 

Оттуда я был в состоянии использовать версию 2.6.6 из ZipFile с интерпретатором Python 2.5.1 (версии 2.7.x неудача на «с» с этой версией ")

Надеется, что это помогает кто-то с помощью древней технологии.

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

  • Нет связанных вопросов^_^