2010-09-23 84 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版本失败的“同”与此版本“)

希望这有助于他人使用古老的技术。