【问题标题】:How to copy from zip file to a folder without unzipping it?如何在不解压缩的情况下从 zip 文件复制到文件夹?
【发布时间】:2020-11-19 20:16:41
【问题描述】:

如何使这段代码有效? 有一个包含文件夹和 .png 文件的 zip 文件。文件夹“.\icons_by_year”为空。我需要一个一个地获取每个文件而不解压缩它并复制到所选文件夹的根目录(所以没有额外的文件夹)。

class ArrangerOutZip(Arranger):
    def __init__(self):
        self.base_source_folder = '\\icons.zip'
        self.base_output_folder = ".\\icons_by_year"

    def proceed(self):
        self.create_and_copy()

    def create_and_copy(self):
        reg_pattern = re.compile('.+\.\w{1,4}$')
        f = open(self.base_source_folder, 'rb')
        zfile = zipfile.ZipFile(f)
        for cont in zfile.namelist():
            if reg_pattern.match(cont):
                with zfile.open(cont) as file:
                    shutil.copyfileobj(file, self.base_output_folder)
        zfile.close()
        f.close()


arranger = ArrangerOutZip()
arranger.proceed()

【问题讨论】:

  • 这能回答你的问题吗? How to move a file?
  • @sahasrara62 不幸的是,不是。我知道如何在两个文件夹之间复制文件。这是从 zip 存档文件中一一复制并将它们粘贴到正确创建的文件夹中的任务。可以像在父类中一样创建文件夹,但我找不到从 zip 文件直接复制绕过解压缩的适当解决方案。
  • 出了什么问题?有回溯消息吗?
  • 问题是info.filename 是压缩文件中的路径,而不是你的文件系统。您只能在 zip 文件打开时复制文件。您可以使用zf.extract 甚至shutil.copyfileobj(zf.open("whatever"), other_fp),但由于路径不在您的文件系统上,shutil.copy2 肯定会失败。我不确定如何重新排列您的代码以在 with open(...) as zf 中进行复制,但这就是需要发生的事情。

标签: python-3.x zip zipfile shutil


【解决方案1】:

shutil.copyfileobj 使用文件对象作为源文件和目标文件。要打开目标,您需要为其构建文件路径。 pathlib 是标准 python 库的一部分,是处理文件路径的好方法。而ZipFile.extract 会为您完成一些创建中间输出目录的工作(加上设置文件元数据),并且可以用来代替copyfileobj

解压缩文件的一个风险是它们可能包含您想要的目标目录之外的绝对或相对路径(例如,“../../badvirus.exe”)。 extract 对此有点过于松懈 - 将这些文件放在目标目录的根目录中 - 所以如果你被搞砸了,我写了一些东西来拒绝整个 zip。

需要几周时间才能使这个程序成为可测试的程序,

from pathlib import Path
import re
import zipfile
#import shutil

#class ArrangerOutZip(Arranger):
class ArrangerOutZip:
    def __init__(self, base_source_folder, base_output_folder):
        self.base_source_folder = Path(base_source_folder).resolve(strict=True)
        self.base_output_folder = Path(base_output_folder).resolve()

    def proceed(self):
        self.create_and_copy()

    def create_and_copy(self):
        """Unzip files matching pattern to base_output_folder, raising
        ValueError if any resulting paths are outside of that folder.
        Output folder created if it does not exist."""
        reg_pattern = re.compile('.+\.\w{1,4}$')
        with open(self.base_source_folder, 'rb') as f:
            with zipfile.ZipFile(f) as zfile:
                wanted_files = [cont for cont in zfile.namelist()
                    if reg_pattern.match(cont)]
                rebased_files = self._rebase_paths(wanted_files,
                    self.base_output_folder)
                for cont, rebased in zip(wanted_files, rebased_files):
                    print(cont, rebased, rebased.parent)
                    # option 1: use shutil
                    #rebased.parent.mkdir(parents=True, exist_ok=True)
                    #with zfile.open(cont) as file, open(rebased, 'wb') as outfile:
                    #    shutil.copyfileobj(file, outfile)
                    # option 2: zipfile does the work for you
                    zfile.extract(cont, self.base_output_folder)
                    
    @staticmethod
    def _rebase_paths(pathlist, target_dir):
        """Rebase relative file paths to target directory, raising
        ValueError if any resulting paths are not within target_dir"""
        target = Path(target_dir).resolve()
        newpaths = []
        for path in pathlist:
            newpath = target.joinpath(path).resolve()
            newpath.relative_to(target) # raises ValueError if not subpath
            newpaths.append(newpath)
        return newpaths


#arranger = ArrangerOutZip('\\icons.zip', '.\\icons_by_year')

import sys
try:
    arranger = ArrangerOutZip(sys.argv[1], sys.argv[2])
    arranger.proceed()
except IndexError:
    print("usage: test.py zipfile targetdir")

【讨论】:

    【解决方案2】:

    我会看一下 zipfile 库的 getinfo() 和 ZipFile.Path() 以进行构造,因为如果您打算进行任何创建,构造函数类也可以以这种方式使用路径。

    特别是PathObjects。能做的就是构造一个包含路径的对象,而且它似乎是基于pathlib的。假设你不需要创建压缩文件,你可以忽略这个 ZipFile.Path()

    但是,这并不是我想要指出的。而是考虑以下几点:

    zipfile.getinfo()

    我认为有一个人在这里遇到了这种确切的情况:

    https://www.programcreek.com/python/example/104991/zipfile.getinfo

    此人似乎正在使用 getinfo() 获取路径。同样清楚的是,并非每个 zipfile 都有信息。

    【讨论】:

    • 我相信这个鳕鱼周围的某个地方有答案,但我无法让它在我的代码中工作: with ZipFile("source", "r") as zf: with zf.open("destination", "w") as entry: shutil.copyfileobj(source, destination)
    • 我改变了主要问题。我更接近解决方案。你现在可以看看吗?我相信我将错误的数据作为 shutil.copyfileobj 中的第二个属性传递。但无法弄清楚如何正确执行此操作 =(
    猜你喜欢
    • 1970-01-01
    • 2012-03-18
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 2014-10-23
    相关资源
    最近更新 更多