deflate --- deflate 压缩与解压缩¶
本模块允许使用 DEFLATE 算法(常用于 zlib 库和 gzip 归档工具)对二进制数据进行压缩和解压缩。自 MicroPython v1.21 起新增。
在 OpenMV 支持的开发板上的可用性:
开发板 |
解压缩 |
压缩 |
|---|---|---|
OpenMV Cam N6 |
是 |
否 |
OpenMV AE3 |
是 |
是 |
OpenMV Cam RT1062 |
是 |
是 |
OpenMV Cam Pure Thermal |
是 |
否 |
OpenMV Cam M4 |
是 |
否 |
OpenMV Cam M7 |
是 |
否 |
OpenMV Cam H7 |
是 |
否 |
OpenMV Cam H7 Plus |
是 |
否 |
Arduino Giga |
是 |
否 |
Arduino Portenta H7 |
是 |
否 |
Arduino Nicla Vision |
是 |
否 |
Arduino Nano 33 BLE Sense |
是 |
否 |
Arduino Nano RP2040 Connect |
是 |
否 |
类¶
- class deflate.DeflateIO(stream: Any, format: int = AUTO, wbits: int = 0, close: bool = False, /)¶
此类可用于包装一个 stream,它可以是任何 类流对象,例如文件、套接字或流(包括
io.BytesIO)。它本身也是一个流,并实现了标准的 read/readinto/write/close 方法。stream 必须是阻塞式流。目前不支持非阻塞式流。
format 可设为下面定义的任何常量,默认为
AUTO。对于解压缩,该值会自动检测 gzip 或 zlib 流;对于压缩,它将生成原始流。wbits 参数设置 DEFLATE 字典窗口大小的以 2 为底的对数。例如,将 wbits 设为
10会将窗口大小设为 1024 字节。有效值为5到15(含),对应于 32 到 32k 字节的窗口大小。如果 wbits 设为
0(默认值),则对于压缩将使用 256 字节的窗口大小(相当于将 wbits 设为 8)。对于解压缩,则取决于格式:RAW将使用 256 字节(相当于将 wbits 设为 8)。ZLIB(或检测到 zlib 的AUTO)将使用 zlib 头部中的值。GZIP(或检测到 gzip 的AUTO)将使用 32 千字节(相当于将 wbits 设为 15)。
有关窗口大小、zlib 和 gzip 流的更多信息,请参阅下方的 窗口大小 说明。
如果 close 设为
True,则当deflate.DeflateIO流关闭时,底层流会被自动关闭。如果你想返回一个包装了另一个流的deflate.DeflateIO流,而又不希望调用方需要了解如何管理底层流,这会很有用。如果启用了压缩功能,则给定的
deflate.DeflateIO实例同时支持读取和写入。例如,可以包装像套接字这样的双向流,从而允许在两个方向上进行压缩/解压缩。
常量¶
这四个 format 常量用于选择应用在原始 deflate 位流外围的封装格式。
- deflate.RAW: int¶
原始 deflate 流(无头部、无尾部、无校验和)。由于该流不包含任何元数据,解压缩器无法从数据中恢复窗口大小,因此在解压缩时应显式设置 wbits——否则默认的 256 字节窗口可能太小。
- deflate.ZLIB: int¶
如 RFC 1950 所定义的 zlib 封装 deflate 流:一个记录窗口大小的 2 字节头部、deflate 载荷,以及一个尾部的 Adler-32 校验和。紧凑且自描述;非常适合嵌入式使用。
- deflate.GZIP: int¶
如 RFC 1952 所定义的 gzip 封装 deflate 流:一个带有可选文件名/时间戳元数据的头部、deflate 载荷,以及尾部的 CRC-32 加未压缩长度。这是由
gzip命令行工具和gzip.GzipFile生成的格式。该头部不记录窗口大小,因此除非设置了 wbits,否则解压缩器必须假定为 32 KiB。
示例¶
deflate.DeflateIO 的一个典型用例是从存储中读取或写入压缩文件:
import deflate
# Writing a zlib-compressed stream (uses the default window size of 256 bytes).
with open("data.z", "wb") as f:
with deflate.DeflateIO(f, deflate.ZLIB) as d:
# Use d.write(...) etc
# Reading a zlib-compressed stream (auto-detect window size).
with open("data.z", "rb") as f:
with deflate.DeflateIO(f, deflate.ZLIB) as d:
# Use d.read(), d.readinto(), etc.
由于 deflate.DeflateIO 是一个流,因此它可以与例如 json.dump() 和 json.load()(以及任何其他可以使用流的地方)一起使用:
import deflate, json
# Write a dictionary as JSON in gzip format, with a
# small (64 byte) window size.
config = { ... }
with open("config.gz", "wb") as f:
with deflate.DeflateIO(f, deflate.GZIP, 6) as f:
json.dump(config, f)
# Read back that dictionary.
with open("config.gz", "rb") as f:
with deflate.DeflateIO(f, deflate.GZIP, 6) as f:
config = json.load(f)
如果你的源数据不是流格式,你可以使用 io.BytesIO 将其转换为适合与 deflate.DeflateIO 一起使用的流:
import deflate, io
# Decompress a bytes/bytearray value.
compressed_data = get_data_z()
with deflate.DeflateIO(io.BytesIO(compressed_data), deflate.ZLIB) as d:
decompressed_data = d.read()
# Compress a bytes/bytearray value.
uncompressed_data = get_data()
stream = io.BytesIO()
with deflate.DeflateIO(stream, deflate.ZLIB) as d:
d.write(uncompressed_data)
compressed_data = stream.getvalue()
Deflate 窗口大小¶
窗口大小限制了(解)压缩器在流中可以向后引用的距离。增大窗口大小会改善压缩效果,但会需要更多内存并使压缩器变慢。
如果某个输入流是用给定的窗口大小压缩的,那么使用较小窗口大小的 DeflateIO 会在解压缩过程中以 OSError 失败,但仅当某个反向引用实际指向的位置比解压缩器的窗口大小更靠前时才会如此。这意味着用较小的窗口大小进行解压缩仍有可能成功。例如,如果原始未压缩数据比窗口大小更短,那么这种情况显然就会成立。
解压缩¶
zlib 格式包含一个头部,用于指定压缩数据时所使用的窗口大小。这表明了解压缩此流所需的最大窗口大小。如果此头部值小于指定的 wbits 值(或者 wbits 未设置),则将使用该头部值。
gzip 格式的头部中不包含窗口大小,并假定所有 gzip 压缩器(例如 gzip 工具,或 CPython 实现的 gzip.GzipFile)都使用 32kiB 的最大窗口大小。因此,如果未设置 wbits 参数,解压缩器将使用 32 kiB 的窗口大小(相当于将 wbits 设为 15)。这意味着,为了能够解压缩任意的 gzip 流,你必须至少拥有这么多可用 RAM。如果你能控制源数据,可以考虑改用具有较小窗口大小的 zlib 格式。
raw 格式没有头部,因此不包含任何关于窗口大小的信息。如果未设置 wbits,则它将默认为 256 字节的窗口大小,这对于给定的流可能不够大。因此,建议在使用 raw 格式时始终显式设置 wbits。
压缩¶
对于压缩,MicroPython 对所有格式都将默认采用 256 字节的窗口大小。这在内存占用极小且压缩速度很快的同时,提供了合理程度的压缩效果,并且会生成可与任何解压缩器配合使用的输出。