Python 文件权限中的 Zipfile

Posted

技术标签:

【中文标题】Python 文件权限中的 Zipfile【英文标题】:Zipfile in Python file permission 【发布时间】:2017-02-19 11:03:34 【问题描述】:

我使用zipfilelib从zip中提取文件,现在解压目录后发现我的文件权限已损坏,

import zipfile
fh = open('sample.zip', 'rb')
z = zipfile.ZipFile(fh)
print z.namelist()
for name in z.namelist():
    z.extract(name, '/tmp/')
fh.close()

但是当我使用linux 解压缩工具时,这个问题不会发生 我尝试使用

os.system('unzip sample.zip')

但我仍然想用zipfile 来做这个

【问题讨论】:

这段代码适合我。我可以有源 zip 文件吗? 制作包含可执行文件的 zip 文件。由于某种原因,我无法上传源 zip 文件。 【参考方案1】:

相关的 Python 问题提供了一些关于为什么首先存在问题的见解:https://bugs.python.org/issue18262 和 https://bugs.python.org/issue15795

此外,可以在此处找到原始 Zip 规范:https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT

重要的部分是:

4.4.2版本(2字节) 4.4.2.1 高字节表示文件的兼容性 属性信息。如果外部文件属性 与 MS-DOS 兼容,可以通过 PKZIP 读取 DOS 版本 2.04g 那么这个值将为零。如果这些 属性不兼容,则此值将 识别属性所在的主机系统 兼容的。软件可以使用此信息来确定 文本文件等的行记录格式。 4.4.2.2 目前的映射是: 0 - MS-DOS 和 OS/2(FAT / VFAT / FAT32 文件系统) 1 - 阿米加 2 - OpenVMS 3 - UNIX 4 - 虚拟机/CMS 5 - 雅达利 ST 6 - OS/2 H.P.F.S. 7 - Macintosh 8 - Z 系统 9 - CP/M 10 - Windows NTFS 11 - MVS (OS/390 - Z/OS) 12 - VSE 13 - Acorn Risc 14 - VFAT 15 - 备用 MVS 16 - BeOS 17 - 串联 18 - OS/400 19 - OS X (Darwin) 20 到 255 - 未使用 ... 4.4.15 外部文件属性:(4字节) 外部属性的映射是 取决于主机系统(请参阅“制作的版本”)。为了 MS-DOS,低位字节是MS-DOS目录 属性字节。如果输入来自标准输入,则此 字段设置为零。

这意味着外部文件属性是系统特定的。解释不同系统的外部文件属性可能会使情况变得更糟。如果我们只关心 UNIX,我们可以检查 ZipInfo.created_system 并将其与 3(对于 UNIX)进行比较。不幸的是,规范并没有进一步帮助我们解释外部属性。

在这个 Wiki 中有一些东西http://forensicswiki.org/wiki/Zip#External_file_attributes

外部属性 UNIX (3) 大小为 4 个字节,包括: ╔═════════╦══════════╦════════╦═══════════════════ ════════════════════════════════════╗ ║ 偏移量 ║ 尺寸 ║ 值 ║ 描述 ║ ╠═════════╬══════════╬════════╬═══════════════════ ═════════════════════════════════════ ║ 0 ║ 1 ║ ║ FAT (MS-DOS) 文件属性。 ║ ║ 1 ║ 1 ║ ║ 未知 ║ ║ 2 ║ 16 位 ║ ║ UNIX 模式(或权限)。 ║ ║ ║ ║ ║ 该值似乎与 stat.st_mode 值相似。 ║ ╚═════════╩══════════╩════════╩═══════════════════ ════════════════════════════════════╝

虽然这只是观察性的,但似乎是共识。

把这些放在一起:

from zipfile import ZipFile

ZIP_UNIX_SYSTEM = 3

def extract_all_with_permission(zf, target_dir):
  for info in zf.infolist():
    extracted_path = zf.extract(info, target_dir)

    if info.create_system == ZIP_UNIX_SYSTEM:
      unix_attributes = info.external_attr >> 16
      if unix_attributes:
        os.chmod(extracted_path, unix_attributes)

with ZipFile('sample.zip', 'r') as zf:
  extract_all_with_permission(zf, '/tmp')

可能会出现一个问题,为什么我们首先要保留权限。有些人可能敢说我们只想保留可执行标志。在这种情况下,一个稍微安全一点的选择可能是只为文件恢复可执行标志。

from zipfile import ZipFile
from stat import S_IXUSR

ZIP_UNIX_SYSTEM = 3

def extract_all_with_executable_permission(zf, target_dir):
  for info in zf.infolist():
    extracted_path = zf.extract(info, target_dir)

    if info.create_system == ZIP_UNIX_SYSTEM and os.path.isfile(extracted_path):
      unix_attributes = info.external_attr >> 16
      if unix_attributes & S_IXUSR:
        os.chmod(extracted_path, os.stat(extracted_path).st_mode | S_IXUSR)

with ZipFile('sample.zip', 'r') as zf:
  extract_all_with_executable_permission(zf, '/tmp')

【讨论】:

您已将同一问题 (bugs.python.org/issue18262) 关联了两次。除此之外,很好的答案! @FabioTurati 感谢您指出这一点。现在已经解决了。【参考方案2】:
import zipfile
import os

unZipFile = zipfile.ZipFile("sample.zip", "r")

tmp_dir = "/tmp"
try:
    for info in unZipFile.infolist():
        real_path = unZipFile.extract(info, tmp_dir)

        # permission
        unix_attributes = info.external_attr >> 16
        target = os.path.join(tmp_dir, info.filename)
        if unix_attributes:
            os.chmod(target, unix_attributes)

        if not real_path:
            print "Extract failed: " + info.filename
finally:
    unZipFile.close()

【讨论】:

回答问题时,请提供与您的代码相关的解释。有些人可能不理解您的代码或看不到它如何回答问题。见how to write a good answer

以上是关于Python 文件权限中的 Zipfile的主要内容,如果未能解决你的问题,请参考以下文章

云中的 Zipfile 文件(amazon s3),无需先将其写入本地文件(无写入权限)

python中的zipfile?

python 将文件夹中的文件递归写入ZIPFile对象

Python 文件 IO 和 zipfile。尝试遍历文件夹中的所有文件,然后使用 Python 遍历相应文件中的文本

使用 python zipfile 提取 zip 子文件夹中的文件

python怎样压缩和解压缩ZIP文件