Извлечь файлы из zip файла и сохранить дату мода?

Я пытаюсь извлечь файлы из zip файла, используя Python 2.7.1 (в Windows, к вашему сведению), и каждая из моих попыток показывает извлеченные файлы с измененной датой = время извлечения (что неверно).

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)

Может кто-нибудь сказать мне, что я делаю не так?

Мне бы хотелось думать, что это возможно без пост-коррекции измененного yime в Как изменить дату создания файла Windows?.

Ответ 1

Ну, это займет немного постобработки, но это не так уж плохо:

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))

Хорошо, может быть, это так плохо.

Ответ 2

На основе ответа 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 до завершения завершения извлечения, поэтому эта версия кэширует имена каталогов и их временные метки до самый конец.

Ответ 3

Основываясь на ответе Ethan Fuman, я разработал эту версию (используя Python 2.6.6), которая немного больше consise:

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() для записи данных вместо создания самого файла.

Ответ 4

На основе ответа 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))

Чтобы сохранить даты, просто вызовите эту функцию после завершения извлечения.

Вот пример, из script я написал zip/unzip директории сохранения игр:

        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]

Спасибо всем за ваши предыдущие ответы!