文件存档、加密和解密

第六章 文件存档、加密和解密

前一章中我们学习了如何处理文件、目录和数据。我们还学习了tarfile模块。本章中,我们将学习文件的存档、加密和解密。存档在管理文件、目录和数据中扮演重要的角色。但首先什么是存档呢?存档是一个将多个文件和目录存放到一个文件中的过程。Python中有tarfile模块可用于创建这类存档文件。

本章中,我们将学习如下课题:

  • 创建和解压存档包
  • Tar包
  • ZIP的创建
  • 文件加密和解密

创建和解压存档包

这一部分中,我们将学习如何使用Python中的shutil模块来创建和解压存档包。shutil模块带有make_archive()函数,可以创建一个新的存档文件。使用make_archive(),我们可以对包含的内容的整个目录进行打包。

创建存档包

下面我们来编写一个名为shutil_make_archive.py的脚本并加入如下内容:

import tarfile, shutil, sys

shutil.make_archive(
        'work_sample', 'gztar',
        root_dir='..',
        base_dir='work'
)
print('Archive contents:')
with tarfile.open('work_sample.tar.gz', 'r') as t_file:
        for names in t_file.getnames():
                print(names)

运行程序,将得到如下输出:

$ python3 shutil_make_archive.py
Archive contents:
work
work/sample.txt

上例中,我们使用了Python中的shutil和tarfile模块来创建存档文件。在shutil.make_archive()中,我们指定了work_sample来作为存档文件的名称并使用 gz 格式。我们还通过基本目录属性指定了工作目录。最后,我们打印出了存档中的文件名称。

解压存档包

要解压存档包,可使用shutil中带有的unpack_archive()函数。使用该函数,我们可以提取存档的文件。我们传递存档包名以及想要提取内容的目录名。如果未传递目录名,就会将提取的内容放到当前工作目录中。

下面创建一个名为shutil_unpack_archive.py的脚本并编写如下代码:

import pathlib, shutil, sys, tempfile

with tempfile.TemporaryDirectory() as d:
        shutil.unpack_archive('work_sample.tar.gz',
                extract_dir='/home/student/work')
        prefix_len = len(d) + 1
        for extracted in pathlib.Path(d).rglob('*'):
                print(str(extracted)[prefix_len:])

运行脚本如下:

$ python3 shutil_unpack_archive.py

下面检查work/目录,会在其中发现work/文件夹,并包含提取的文件。

Tar包

这一部分我们将学习tarfile模块。我们还将学习输入文件名的测试,评估它是否是有效的存档文件名。我们会看如何将一个新文件加入到已有的存档文件中,如何使用tarfile模块读取元数据,以及如何使用extractall() 函数来从存档中提取文件。

首先,我们将测试输入的文件名是否是有效的存档文件。进行这一测试,tarfile模块带有is_tarfile()函数,返回的是布尔值。

创建一个名为check_archive_file.py的脚本并编写如下内容:

import tarfile

for f_name in ['hello.py', 'work.tar.gz', 'welcome.py', 'nofile.tar', 'sample.tar.xz']:
        try:
                print('{:} {}'.format(f_name, tarfile.is_tarfile(f_name)))
        except IOError as err:
                print('{:} {}'.format(f_name, err))

运行脚本,将得到如下输出:

$ python3 check_archive_file.py
hello.py False
work.tar.gz True
welcome.py False
nofile.tar [Errno 2] No such file or directory: 'nofile.tar'
sample.tar.xz False

因此,tarfile.is_tarfile()会检查列表出的各个文件名。hello.py, welcome.py文件不是tar包,所以得到的是布尔值False,work.tar.gz和sample.tar.xz是tar包,因此得到了布尔值True。并且目录中不存在nofile.tar这一文件,所以抛出了脚本中所编写的异常。

下面我们将向已创建的存档文件中添加一个新文件。创建名为add_to_archive.py的脚本并编写如下代码:

import shutil, os, tarfile

print('creating archive')
shutil.make_archive('work', 'tar', root_dir='..', base_dir='work',)
print('\nArchive contents:')
with tarfile.open('work.tar', 'r') as t_file:
        for names in t_file.getnames():
                print(names)
        os.system('touch sample.txt')
        print('adding sample.txt')
        with tarfile.open('work.tar', mode='a') as t:
                t.add('sample.txt')
        print('contents:',)
        with tarfile.open('work.tar', mode='r') as t:
                print([m.name for m in t.getmembers()])

运行脚本,我们将得到如下输出:

$ python3 add_to_archive.py

# 输出结果:
creating archive

Archive contents:
work
work/work
work/work/sample.txt
adding sample.txt
contents:
['work', 'work/work', 'work/work/sample.txt', 'sample.txt']

本例中,首先我们使用shutil.make_archive()创建了一个压缩文件,然后我们打印出了存档文件中的内容。再后我们在下一条语句中创建了一个sample.txt文件。接着我们希望将sample.txt加到已创建的work.tar中。这里我们使用了追加模式。接下来我们再次读取了存档文件中的内容。

下面我们将学习如何读取存档文件的元数据。getmembers() 会加载文件的元数据。创建一个名为read_metadata.py的脚本并编写如下内容:

import tarfile, time

with tarfile.open('work.tar', 'r') as t:
        for file_info in t.getmembers():
                print(file_info.name)
                print('Size :', file_info.size, 'bytes')
                print('Type :', file_info.type)
                print()

运行脚本,将会得到如下输出:

$ python3 read_metadata.py
work
Size : 0 bytes
Type : b'5'

work/work
Size : 0 bytes
Type : b'5'

work/work/sample.txt
Size : 0 bytes
Type : b'0'

sample.txt
Size : 0 bytes
Type : b'0'

下面我们将使用 extractall() 函数来从存档包中提取内容。为此,创建一个名为extract_contents.py的脚本并在其中编写如下代码:

import tarfile, os

os.mkdir('work')
with tarfile.open('work.tar', 'r') as t:
        t.extractall('work')
print(os.listdir('work'))

运行脚本,将得到如下输出:

$ python3 extract_contents.py

# 输出结果:
['sample.txt', 'work']

查看当前工作目录,我们会看到work/ 目录,进入该目录就可以看到提取的文件。

ZIP的创建

这部分中,我们将使用ZIP文件。我们将学习Python中的zipfile模块,如何创建ZIP文件,如何测试输入的文件名是否是有效的ZIP文件名,读取元数据等等。

首先我们将学习如何使用shutil模块的make_archive()函数创建zip文件。创建一个名为make_zip_file.py的脚本并编写如下代码:

import shutil
shutil.make_archive('work', 'zip', 'work')

运行脚本如下:

$ python3 make_zip_file.py

此时查看当前工作目录,就会发现work.zip文件。

接下来我们将测试所输入的zip文件名是否有效。为此可使用zipfile模块中的is_zipfile()函数。

创建一个脚本check_zip_file.py并编写如下内容:

import zipfile

for f_name in ['hello.py', 'work.zip', 'welcome.py', 'sample.txt', 'test.zip']:
        try:
                print('{:}        {}'.format(f_name, zipfile.is_zipfile(f_name)))
        except IOError as err:
                print('{:}        {}'.format(f_name, err))

运行脚本如下:

$ python3 check_zip_file.py

# 输出结果:
hello.py        False
work.zip        True
welcome.py        False
sample.txt        False
test.zip        False

在本例中,我们使用了for循环来检查列表中的文件名。is_zipfile()函数会逐一检查文件名并在结果中返回布尔值。

下面我们将来看如何使用Python中的zipfile模块来读取存档的ZIP文件的元数据。创建一个名为read_zip_metadata.py的脚本,并编写如下内容:

import zipfile

def meta_info(names):
        with zipfile.ZipFile(names) as zf:
                for info in zf.infolist():
                        print(info.filename)
                        if info.create_system == 0:
                                system = 'Windows'
                        elif info.create_system == 3:
                                system = 'Unix'
                        else:
                                system = 'UNKNOWN'
                        print('System      :', system)
                        print('Zip Version :', info.create_version)
                        print('Compressed  :', info.compress_size, 'bytes')
                        print('Uncompressed:', info.file_size, 'bytes')
                        print()

if __name__ == "__main__":
        meta_info('work.zip')

执行脚本如下:

$ python3 read_zip_metadata.py
make_zip_file.py
System      : Unix
Zip Version : 20
Compressed  : 49 bytes
Uncompressed: 57 bytes

sample.txt
System      : Unix
Zip Version : 20
Compressed  : 2 bytes
Uncompressed: 0 bytes

add_to_archive.py
System      : Unix
Zip Version : 20
Compressed  : 240 bytes
Uncompressed: 483 bytes

shutil_unpack_archive.py
System      : Unix
Zip Version : 20
Compressed  : 196 bytes
Uncompressed: 269 bytes

要获取zip文件的元数据信息,我们使用了ZipFile类的infolist()方法。

文件加密和解密

这一部分中我们将学习Python中的pyAesCrypt模块。pyAesCrypt是一个文件加密模块,使用AES256-CBC算法来对文件和二进制流文件加密和解密。

安装pyAesCrypt命令如下:

pip3 install pyAesCrypt

创建名为file_encrypt.py的脚本并编写如下代码:

import pyAesCrypt

from os import stat, remove
# encryption/decryption buffer size - 64K
bufferSize = 64 * 1024
password = '#Training'
with open('sample.txt', 'rb') as fIn:
        with open('sample.txt.aes', 'wb') as fOut:
                pyAesCrypt.encryptStream(fIn, fOut, password, bufferSize)
# get encrypted file size
encFileSize = stat('sample.txt.aes').st_size

运行脚本如下:

$ python3 file_encrypt.py

# 无输入内容

请查看当前工作目录,我们会发现其中有一个sample.txt.aes文件。

本例中,我们首先的提及了缓冲大小和密码。然后涉及了需要加密的文件名。在encryptStream中,我们传入了加密的文件fIn,以及加密后的文件名fOut。我们将加密后的文件存储为sample.txt.aes。

下面我们将对sample.txt.aes文件进行解密来获取文件的内容。创建一个名为file_decrypt.py 的脚本并编写如下内容:

import pyAesCrypt
from os import stat, remove

bufferSize = 64 * 1024
password = '#Training'
encFileSize = stat('sample.txt.aes').st_size
with open('sample.txt.aes', 'rb') as fIn:
        with open('sampleout.txt', 'wb') as fOut:
                try:
                        pyAesCrypt.decryptStream(fIn, fOut, password, bufferSize, encFileSize)
                except ValueError:
                        remove('sampleout.txt')

运行脚本如下:

$ python3 file_decrypt.py

现在查看当前工作目录。一个名为sampleout.txt的文件会被创建。这就是我们的解密后文件。

本例中,我们涉及到了解密的文件名sample.txt.aes。然后,我们解密后的文件为sampleout.txt。在decryptStream中,涉及到了要解密的文件fIn,以及解密后文件的名称fOut。

总结

本章中,我们学习了创建和提取存档文件。在管理文件、目录和数据时存档扮演着重要的角色。它还将许多文件和目录存入一个文件中。

我们深入学习了Python模块tarfile和zipfile,来让我们创建、提取和测试存档文件。我们可以将新文件添加到已有存档文件中、读取元数据、从存档中提取文件。我们还学习了使用pyAescrypt模块来进行文件加密和解密。

下一章中我们将学习Python 中的文本处理和正则表达式。Python有一称作正则表达式的非常强大的库,可以完成搜索和提取数据等任务。

课后问题

  1. 我们是否可以用加密的方式压缩数据?如果可以,如何进行?
  2. Python中的上下文管理器是什么?
  3. 什么是pickling和unpickling?
  4. Python中不函数类型有哪些?

扩展阅读

  • 数据压缩和存档: https://docs.python.org/3/library/archiving.html
  • tempfile文档: https://docs.python.org/2/library/tempfile.html
  • Python加密文档: https://docs.python.org/3/library/crypto.html
  • shutil文档: https://docs.python.org/3/library/shutil.html

你可能感兴趣的:(python,linux,运维,服务器)