如何用Python3 ZipFile的writestr写入GBK编码文件名到压缩包
解决Zip压缩包文件名适配GBK编码Windows系统的问题
方法一:Python 3.7+ 直接使用encoding参数
Python 3.7及以上的zipfile.ZipFile新增了encoding参数,可直接指定文件名的编码为GBK,同时会自动跳过UTF-8标志位的设置,让旧Windows系统用本地GBK编码解析文件名。
代码示例:
import zipfile import io # 创建内存中的压缩包,指定文件名编码为GBK with io.BytesIO() as buffer: with zipfile.ZipFile(buffer, 'w', zipfile.ZIP_DEFLATED, encoding='gbk') as zf: # 文件名使用Python原生str(UTF-8),会自动转成GBK字节写入 filename = "中文测试文件.txt" # 文件内容以UTF-8字节直接写入,无需额外处理 file_content = "这是UTF-8编码的文件内容".encode('utf-8') zf.writestr(filename, file_content) # 将内存中的压缩包数据读取出来,用于上传服务器 buffer.seek(0) zip_data = buffer.read() # 这里添加上传服务器的逻辑,比如requests.post(..., data=zip_data)
方法二:兼容Python 3.6及以下版本(继承ZipFile重写逻辑)
如果需要兼容更低版本的Python,可以通过继承ZipFile类,手动处理文件名的GBK编码和UTF-8标志位的清除:
import zipfile import io class GBKZipFile(zipfile.ZipFile): def __init__(self, file, mode='r', compression=zipfile.ZIP_STORED, allowZip64=True): super().__init__(file, mode, compression, allowZip64) # 强制文件名编码为GBK self._encoding = 'gbk' def writestr(self, zinfo_or_arcname, data, compress_type=None): if isinstance(zinfo_or_arcname, str): # 创建ZipInfo对象并清除UTF-8标志位 zinfo = zipfile.ZipInfo(zinfo_or_arcname) zinfo.compress_type = compress_type or self.compression # 清除第11位的UTF-8标志位 zinfo.flag_bits &= ~0x800 zinfo_or_arcname = zinfo super().writestr(zinfo_or_arcname, data) # 使用示例 with io.BytesIO() as buffer: with GBKZipFile(buffer, 'w', zipfile.ZIP_DEFLATED) as zf: filename = "中文测试文件.txt" file_content = "这是UTF-8编码的文件内容".encode('utf-8') zf.writestr(filename, file_content) buffer.seek(0) zip_data = buffer.read() # 上传服务器逻辑
原理说明
Zip压缩包规范中,文件名的编码由flag_bits的第11位(0x800)决定:
- 若该位为1,文件名采用UTF-8编码
- 若该位为0,文件名采用本地系统编码(旧Windows默认GBK)
通过指定encoding='gbk'或手动清除UTF-8标志位并强制GBK编码,就能让GBK编码的Windows系统正确解析文件名,同时文件内容保持UTF-8编码不受影响。
内容的提问来源于stack exchange,提问作者Notealot
相关产品推荐
相关产品推荐

