从zip文件中提取文件并保留修改日期?

28
我正在尝试使用Python 2.7.1(在Windows上,供您参考)从zip文件中提取文件,但我所有的尝试都显示提取的文件的修改日期 = 提取时间(这是不正确的)。
import os,zipfile
outDirectory = 'C:\\_TEMP\\'
inFile = 'test.zip'
fh = open(os.path.join(outDirectory,inFile),'rb') 
z = zipfile.ZipFile(fh)
for name in z.namelist():
    z.extract(name,outDirectory)
fh.close()
我也尝试使用.extractall方法,结果相同。
import os,zipfile
outDirectory = 'C:\\_TEMP\\'
inFile = 'test.zip'
zFile = zipfile.ZipFile(os.path.join(outDirectory,inFile))        
zFile.extractall(outDirectory)

有人能告诉我我做错了什么吗?

我想不修改文件上次修改时间的情况下也能实现这样,如何更改Windows文件的创建日期?


5
你会非常失望的... - Ignacio Vazquez-Abrams
2
即使对于Python 3.X版本,这似乎仍然是一个问题。 - Trilarion
4个回答

20

好的,它确实需要一些后处理,但并不那么糟糕:

import os
import zipfile
import time

outDirectory = 'C:\\TEMP\\'
inFile = 'test.zip'
fh = open(os.path.join(outDirectory,inFile),'rb') 
z = zipfile.ZipFile(fh)

for f in z.infolist():
    name, date_time = f.filename, f.date_time
    name = os.path.join(outDirectory, name)
    with open(name, 'wb') as outFile:
        outFile.write(z.open(f).read())
    date_time = time.mktime(date_time + (0, 0, -1))
    os.utime(name, (date_time, date_time))

好吧,也许它确实很糟糕。


1
可以,谢谢。只是为了澄清一下,保持文件提取的修改日期是Python zipfile实现的限制还是所有zip库的标准功能? - MTAdmin
1
距离回复已经过去了5年,它仍然在Python 3.6下运行。它看起来和你写的那天一样丑陋,但是它能正常工作。 - EndermanAPM
Python 3.6还需要这个吗?从源代码来看,我没有看到任何与日期/时间信息相关的更改... - RvdK
2
@RvdK:添加(0, 0, -1)是因为time.mktime需要一个9元素的元组。-1表示DST标志未知。 - Ethan Furman
获取压缩文件的时区信息是否可能?如果从具有不同时区的服务器下载zip文件,则文件的修改时间将无法按预期保留。 - wypul
显示剩余3条评论

9

根据Jia103的回答,我开发了一个函数(使用Python 2.7.14),它可以在所有内容被提取后保留目录和文件日期。这样可以隔离函数中的任何不美观之处,并且您还可以使用zipfile.Zipfile.extractAll()或任何您想要的zip提取方法:

import time
import zipfile
import os

# Restores the timestamps of zipfile contents.
def RestoreTimestampsOfZipContents(zipname, extract_dir):
    for f in zipfile.ZipFile(zipname, 'r').infolist():
        # path to this extracted f-item
        fullpath = os.path.join(extract_dir, f.filename)
        # still need to adjust the dt o/w item will have the current dt
        date_time = time.mktime(f.date_time + (0, 0, -1))
        # update dt
        os.utime(fullpath, (date_time, date_time))

只需要在提取完成后调用此函数即可保留日期。以下是一个示例,来自我编写的用于压缩/解压游戏存档目录的脚本:
        z = zipfile.ZipFile(zipname, 'r')
        print 'I have opened zipfile %s, ready to extract into %s' \
                % (zipname, gamedir)
        try: os.makedirs(gamedir)
        except: pass    # Most of the time dir already exists
        z.extractall(gamedir)
        RestoreTimestampsOfZipContents(zipname, gamedir)  #<--  USED
        print '%s zip extract done' % GameName[game]

感谢大家之前的回答!

8

基于Ethan Fuman的回答,我开发了这个版本(使用Python 2.6.6),它更加简洁:

zf = ZipFile('archive.zip', 'r')
for zi in zf.infolist():
    zf.extract(zi)
    date_time = time.mktime(zi.date_time + (0, 0, -1))
    os.utime(zi.filename, (date_time, date_time))
zf.close()

这将提取到当前工作目录,并使用ZipFile.extract()方法来写入数据,而不是创建文件本身。

我正在尝试使用这段代码,但所有文件都保存在我的根目录中。我应该在哪里定义我想要保存文件的路径? - new_programmer_22
@newwebdev22 代码提取到当前目录。你可以更改它,或者根据文档中所述,在extract()方法中添加路径参数:https://docs.python.org/2/library/zipfile.html#zipfile.ZipFile.extract 此外,你可能需要检查ZIP归档中的绝对路径。 - Ber

6

根据Ber的回答,我开发了这个版本(使用Python 2.7.11),还考虑了目录修改日期。

from os import path, utime
from sys import exit
from time import mktime
from zipfile import ZipFile

def unzip(zipfile, outDirectory):
    dirs = {}

    with ZipFile(zipfile, 'r') as z:
        for f in z.infolist():
            name, date_time = f.filename, f.date_time
            name = path.join(outDirectory, name)
            z.extract(f, outDirectory)

            # still need to adjust the dt o/w item will have the current dt
            date_time = mktime(f.date_time + (0, 0, -1))

            if (path.isdir(name)):
                # changes to dir dt will have no effect right now since files are
                # being created inside of it; hold the dt and apply it later
                dirs[name] = date_time
            else:
                utime(name, (date_time, date_time))

    # done creating files, now update dir dt
    for name in dirs:
       date_time = dirs[name]
       utime(name, (date_time, date_time))

if __name__ == "__main__":

    unzip('archive.zip', 'out')

    exit(0)

由于在创建文件时目录正在被修改,因此在使用os.utime设置其日期之前似乎没有意义,因此此版本将缓存目录名称及其时间戳直到最后。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接