.zip 文件在使用 gmail api 发送并使用 zlib 压缩时损坏
.zip file gets corrupted when sent with gmail api and compressed with zlib
我正在使用 Python 3.7 并使用 python 的 zipfile
和 zlib
压缩一个 .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。您可以按如下方式进行:
- 导入
encoders
模块
from email import encoders
- 在您的
_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
我正在使用 Python 3.7 并使用 python 的 zipfile
和 zlib
压缩一个 .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。您可以按如下方式进行:
- 导入
encoders
模块
from email import encoders
- 在您的
_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