Python脚本之ZIP文件压缩、解压

本文为博主原创,未经许可严禁转载。
本文链接:https://blog.csdn.net/zyooooxie/article/details/114632112

最近在翻看Python的官方文档,https://docs.python.org/zh-cn/3.7/library/archiving.html ,做些分享;

【本文 实操是 用的 大小不超5M的文件】

个人博客:https://blog.csdn.net/zyooooxie

需求

  1. 对某文件、某文件list、某文件夹 压缩、解压
  2. 对某文件、某文件list、某文件夹 使用特定密码 压缩、解压

压缩

https://docs.python.org/zh-cn/3.7/library/zipfile.html

zipfile支持解密 ZIP归档中的加密文件,但是目前不能创建一个加密的文件。我的思路是 用命令行 调用本地EXE来加密压缩;【目前是用的7z;https://www.7-zip.org/



def exe_zip(command_str: str):
    """
    Run command:7z
    :param command_str:
    :return:
    """
    # 使用7z;设置环境变量,加了Path

    Log.info(command_str)

    cp = subprocess.run(args=command_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8")

    print(cp.stdout)
    Log.info(cp.returncode)
    assert cp.returncode == 0


def write_one_file(file_path: str, zip_file_name: str, password: str = None):
    """
    压缩一个文件
    :param file_path:
    :param zip_file_name:
    :param password:
    :return:
    """
    Log.info('待压缩文件:{},密码:{}'.format(file_path, password))

    if password is None:
        # compression:写zip文档时 使用的压缩方法;
        # mode:可选 r,w,a 代表不同的打开文件的方式;r 只读;w 重写\新建;a 添加;
        # 要操作的zip文件大小超过2G,应该将allowZip64设置为True;

        with zipfile.ZipFile(zip_file_name, mode='w', compression=zipfile.ZIP_DEFLATED) as zf:
            # arcname 归档名称应当是基于归档根目录的相对路径,也就是说,它们不应以路径分隔符开头;
            zf.write(file_path, arcname=os.path.basename(file_path))

    else:
        # zf.setpassword(password.encode())       # 是无法设置密码的

        command_str = ' '.join(['7z', 'a', zip_file_name, '-p{}'.format(password), '{}'.format(file_path)])
        exe_zip(command_str)


def write_many_files(file_path: list, zip_file_name: str, password: str = None):
    """
    压缩多个文件
    :param file_path:
    :param zip_file_name:
    :param password:
    :return:
    """
    Log.info('要压缩的文件list:{},密码:{}'.format(file_path, password))

    if password is None:
        with zipfile.ZipFile(zip_file_name, mode='w', compression=zipfile.ZIP_DEFLATED) as zf:
            for f in file_path:
                zf.write(f, arcname=os.path.basename(f))

    else:
        new_file_path = ' '.join(file_path)
        print(new_file_path)

        command_str = ' '.join(['7z', 'a', zip_file_name, '-p{}'.format(password), '{}'.format(new_file_path)])
        exe_zip(command_str)


def write_folder(file_path: str or list, zip_file_name: str, password: str = None, zip_dirs: str = 'yes',
                 use_7z: str = 'no'):
    """
    压缩一个文件夹;
    有、无密码,7z都会保持原目录;
    无密码时,zipfile可设置 是否保持原目录;
    :param file_path:
    :param zip_file_name:
    :param password:
    :param zip_dirs:
    :param use_7z:
    :return:
    """
    Log.info('待压缩的目录:{},密码:{}'.format(file_path, password))

    if password is None and use_7z == 'no':

        with zipfile.ZipFile(zip_file_name, mode='w', compression=zipfile.ZIP_DEFLATED) as zf:
            for root, dirs, files in os.walk(file_path):

                # root 是 改变的
                # print(root)

                f_path = root.replace(file_path, '')

                for f in files:

                    # print(os.path.join(root, f))
                    # print(os.path.join(f_path, f))

                    if zip_dirs == 'yes':
                        # 文件的全路径, 文件名 [多层目录]
                        zf.write(os.path.join(root, f), arcname=os.path.join(f_path, f))

                    else:
                        # 一层目录
                        zf.write(os.path.join(root, f), arcname=f)

    else:
        # -r 这个参数表示遍历所有的子目录,每个文件都执行压缩操作,添加到压缩文件中

        # \* 该目录下的所有的文件
        file_path += r'\*'

        if password is None:
            # 不使用密码
            command_str = ' '.join(['7z', 'a', zip_file_name, '{}'.format(file_path)])
        else:
            # 使用密码
            command_str = ' '.join(['7z', 'a', zip_file_name, '-p{}'.format(password), '{}'.format(file_path)])

        exe_zip(command_str)


def test_write(file_path: str or list, password: str = None, encrypted_file_path: str = None):
    """
    压缩文件
    :param file_path:
    :param password:
    :param encrypted_file_path: 不要用中文路径
    :return:
    """

    zip_file_name = 'zy.zip'

    if encrypted_file_path is not None:
        zip_file_name = os.path.join(encrypted_file_path, zip_file_name)

    if isinstance(file_path, list):
        # 多个文件
        write_many_files(file_path, zip_file_name, password)

    elif os.path.isfile(file_path):
        # 一个文件
        write_one_file(file_path, zip_file_name, password)

    elif os.path.isdir(file_path):
        # 一个文件夹

        # 无密码
        # write_folder(file_path, zip_file_name, password)                        # zipfile 多层
        # write_folder(file_path, zip_file_name, password, zip_dirs='no')         # zipfile 一层
        # write_folder(file_path, zip_file_name, password, use_7z='yes')          # 7z 无密码

        # 有密码
        write_folder(file_path, zip_file_name, password, use_7z='yes')          # 7z 有密码

    # 压缩多个文件夹的场景,暂时没遇到

    else:
        raise Exception('file_path 不合法')

    Log.info('压缩文件:{} 新建成功'.format(zip_file_name))


解压


def test_extractall_zipfile(encrypted_file_path: str, password: str = None, target_dir: str = None):
    """
    zipfile解压
    :param encrypted_file_path:
    :param password:
    :param target_dir:
    :return:
    """
    Log.info('待解压的 zip文件:{},密码:{}'.format(encrypted_file_path, password))

    if zipfile.is_zipfile(encrypted_file_path):
        with zipfile.ZipFile(encrypted_file_path, 'r') as zf:
            Log.info('文件列表:{}'.format(zf.namelist()))

            if target_dir is not None:
                # `path' specifies a different directory to extract to.

                if password is None:
                    zf.extractall(path=target_dir)
                else:
                    zf.extractall(pwd=password.encode(), path=target_dir)
            else:

                # 当前工作目录
                if password is None:
                    zf.extractall()
                else:
                    zf.extractall(pwd=password.encode())

    # 中文乱码【Windows系统】
    # 修改zipfile的源码 decode('cp437')改为decode('gbk')

    Log.info('解压文件夹:{} 解压成功'.format(target_dir))


def test_extractall_7z(encrypted_file_path: str, password: str = None, target_dir: str = None):
    """
    7z解压
    :param encrypted_file_path:
    :param password:
    :param target_dir:
    :return:
    """
    Log.info('待解压的 zip文件:{},密码:{}'.format(encrypted_file_path, password))

    if zipfile.is_zipfile(encrypted_file_path):

        if target_dir is None:
            target_dir = os.path.abspath(os.getcwd())

        if password is not None:
            # x:将压缩档案中的所有文件解压到指定路径,压缩包内的文件所在的目录结构保持不变
            # e:将压缩档案中的所有文件解压到指定路径,所有文件将输出到同一个目录中
            # -aoa:直接覆盖现有文件,而没有任何提示

            command_str = ' '.join(['7z', 'x', encrypted_file_path, '-p{}'.format(password), '-o{}'.format(target_dir), '-aoa'])
        else:
            command_str = ' '.join(['7z', 'x', encrypted_file_path, '-o{}'.format(target_dir), '-aoa'])

        exe_zip(command_str)

    Log.info('解压文件夹:{} 解压成功'.format(target_dir))

本文链接:https://blog.csdn.net/zyooooxie/article/details/114632112

交流技术 欢迎+QQ 153132336 zy
个人博客 https://blog.csdn.net/zyooooxie

Guess you like

Origin blog.csdn.net/zyooooxie/article/details/114632112