2010-09-23 80 views
4

我想從壓縮文件中提取文件到特定路徑,忽略存檔中的文件路徑。這是在Python 2.6非常簡單(我的文檔字符串比代碼更長)如何在Python 2.5中模擬ZipFile.open?

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,我想我只需要解析並跳過頭結構以自己到達文件偏移量。使用維基百科作爲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()) 

注我怎樣解包和讀,賦予額外的字段的長度的字段中,因爲在屬性ZipInfo.extra主叫len給出4個字節以下,從而使所述偏移被錯誤地計算。也許我在這裏錯過了一些東西?

任何人都可以改進此解決方案爲Python 2.5?

編輯:我應該說,顯而易見的解決辦法的建議通過ChrisAdams因爲它試圖發出聲音整個文件到內存

dest_file.write(zip_file.read(name)) 

將失敗,並MemoryError爲包含在ZIP任何合理大小的文件一氣呵成。我有大文件,所以我需要將內容流出到磁盤。另外,升級Python是顯而易見的解決方案,但是完全不在我的手中,實際上不可能。

+0

你爲什麼不能升級?2.7是最新的t他2.x系列,你很過時......沒有理由留在舊版 – Daenyth 2010-09-23 18:22:36

+2

@Daenyth我只希望。嘗試告訴負責管理x00服務器的操作團隊... – Day 2010-09-23 20:46:19

回答

0

由於我的限制,它看起來像答案在我的問題給出:解析zip文件結構自己和使用zlib.decompressobj一旦找到它們就解壓縮字節。

如果你沒有(/苦於)我的約束,你可以在這裏找到更好的答案:

  1. 如果可以,只是升級的Python 2.5到2.6,如建議(或更高版本!) Daenyth的評論。
  2. 如果你只有在可以在內存中加載了100%的zip小文件,使用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)將因爲任何包含在zip中的合理大小的文件而導致MemoryError失敗,因爲它會嘗試將整個內容整合到一個內存中走。我需要把它流出來。應該在問題中提到這個問題,但是謝謝你的建議。 – Day 2010-09-23 20:47:21

1

我知道我對這個問題晚了一點,但是有完全相同的問題。

我使用的解決方案是複製python 2.6。第6版的壓縮文件,並把文件夾中(我把它叫做python_fix)和進口,與其:

python_fix/zipfile.py 
代碼

然後:

import python_fix.zipfile as zipfile 

從那裏我可以使用2.6.6版本使用Python 2.5.1解釋的壓縮文件中(在2.7.X版本失敗的「同」與此版本「)

希望這有助於他人使用古老的技術。