.zip 文件在使用 gmail api 发送并使用 zlib 压缩时损坏

.zip file gets corrupted when sent with gmail api and compressed with zlib

我正在使用 Python 3.7 并使用 python 的 zipfilezlib 压缩一个 .csv 文件。

import zipfile

filename = "report.csv"

zip_filename = f"{filename[:-4]}.zip"
with zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) as zip:
    zip.write(filename)

然后将 zip 文件附加到电子邮件中,我有一些逻辑来确定它的 MIME 类型(我已经检查它正确地确定它是 application/zip):

def _make_attachment_part(self, filename: str) -> MIMEBase:
    content_type, encoding = mimetypes.guess_type(filename)
    if content_type is None or encoding is not None:
        content_type = "application/octet-stream"

    main_type, sub_type = content_type.split("/", 1)
    msg = MIMEBase(main_type, sub_type)
    with open(filename, "rb") as f:
        msg.set_payload(f.read())

    base_filename = os.path.basename(filename)
    msg.add_header("Content-Disposition", "attachment", filename=base_filename)

    return msg

然后,为MIMEMultipart类型的message设置主题、收件人、抄送、附件等。然后,我使用 base64 进行编码并发送。

raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()

我收到的附件名称正确且大小符合预期,但是,当我尝试使用 unzip file.zip 时,出现以下错误:

error [file.zip]:  missing 5 bytes in zipfile

有人知道我做错了什么吗?事实上,电子邮件是从 Ubuntu 机器发送的,而我试图在 MacOS 上打开收到的文件。

定义在rfc1341:

An encoding type of 7BIT requires that the body is already in a seven-bit mail- ready representation. This is the default value -- that is, "Content-Transfer-Encoding: 7BIT" is assumed if the Content-Transfer-Encoding header field is not present.

在您的例子中,在 _make_attachment_part 函数中,您将有效负载设置为您的 MIMEBase 对象,但您没有指定 Content-Transfer-Encoding。

我建议您将负载编码为 Base64。您可以按如下方式进行:

  1. 导入encoders模块
from email import encoders
  1. 在您的 _make_attachment_part 函数中,使用 encoders 模块对您的负载进行编码。
def _make_attachment_part(self, filename: str) -> MIMEBase:
    content_type, encoding = mimetypes.guess_type(filename)
    if content_type is None or encoding is not None:
        content_type = "application/octet-stream"

    main_type, sub_type = content_type.split("/", 1)
    msg = MIMEBase(main_type, sub_type)
    with open(filename, "rb") as f:
        msg.set_payload(f.read())

    encoders.encode_base64(msg) # NEW

    base_filename = os.path.basename(filename)
    msg.add_header("Content-Disposition", "attachment", filename=base_filename)

    return msg