如何在不解压缩的情况下从 zip 文件复制到文件夹?

How to copy from zip file to a folder without unzipping it?

如何使这段代码有效? 有一个包含文件夹和 .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()

我会看一下 zipfile 库的 getinfo() 和 ZipFile.Path() 进行构造,因为构造函数 class 也可以那样使用路径,如果你打算做任何创作。

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

然而,这并不是我想要指出的。而是考虑以下内容:

zipfile.getinfo()

我认为这里有一个人正在处理这种情况:

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

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

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