使用Python无需下载至本地直接发送S3内CSV文件为邮件附件
可行性结论
该需求完全可以实现,全程不需要依赖本地存储,也不需要把S3文件下载到磁盘,所有文件读取、附件构造操作全在内存中完成即可,完全适配你在常规服务器上运行Python应用的场景,和是否使用Lambda没有关系。
核心逻辑
你之前参考的方案落盘的本质,是调用了S3 SDK的下载到本地文件的方法,先把文件写到磁盘路径,再从磁盘读文件构造邮件附件,这一步磁盘IO完全可以跳过:
- S3 SDK的
get_object接口会直接返回可读取的流式响应体,不需要落盘就能拿到完整文件内容 - Python原生的邮件构造模块支持直接读取内存中的二进制内容生成附件,不需要从本地路径加载文件
两者直接对接就能实现零本地存储依赖的发送逻辑。
实现步骤
- 安装依赖:只需要安装
boto3用于对接S3,邮件发送相关能力全部用Python标准库实现即可,执行安装命令pip install boto3 - 初始化S3客户端,配置好对应存储桶的访问凭证、区域、访问地址
- 调用
get_object接口获取目标CSV对象,不要使用download_file这类会写本地磁盘的方法 - 直接读取响应体中的二进制内容,存入内存字节对象
- 构造标准MIME邮件,将内存中的CSV二进制内容封装为附件,设置好附件文件名
- 通过SMTP协议完成邮件发送,全程不会产生任何本地文件写入操作
可直接运行的参考代码
import boto3 import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication # 替换为你自己的配置项 S3_CONF = { "aws_access_key_id": "你的S3访问密钥ID", "aws_secret_access_key": "你的S3访问密钥", "region_name": "存储桶所在区域" } TARGET_BUCKET = "你的S3存储桶名称" CSV_S3_KEY = "目标CSV在桶内的完整路径,例:report/202405/sales.csv" MAIL_CONF = { "smtp_host": "你的SMTP服务器地址", "smtp_port": 465, "smtp_account": "发件邮箱账号", "smtp_password": "发件邮箱授权码", "sender_addr": "发件人邮箱地址", "receiver_addrs": ["收件人1邮箱", "收件人2邮箱"] } def send_csv_as_attachment(): # 初始化S3客户端 s3 = boto3.client("s3", **S3_CONF) # 拉取S3对象,全程不落盘 s3_resp = s3.get_object(Bucket=TARGET_BUCKET, Key=CSV_S3_KEY) # 直接把文件内容读入内存 csv_binary = s3_resp["Body"].read() csv_filename = CSV_S3_KEY.split("/")[-1] # 构造邮件内容 mail = MIMEMultipart() mail["From"] = MAIL_CONF["sender_addr"] mail["To"] = ",".join(MAIL_CONF["receiver_addrs"]) mail["Subject"] = "CSV数据文件" mail.attach(MIMEText("请查收附件中的CSV数据文件", "plain", "utf-8")) # 构造附件,直接使用内存中的二进制内容 attachment = MIMEApplication(csv_binary) attachment.add_header( "Content-Disposition", "attachment", filename=csv_filename ) mail.attach(attachment) # 发送邮件 with smtplib.SMTP_SSL(MAIL_CONF["smtp_host"], MAIL_CONF["smtp_port"]) as smtp_conn: smtp_conn.login(MAIL_CONF["smtp_account"], MAIL_CONF["smtp_password"]) smtp_conn.sendmail( MAIL_CONF["sender_addr"], MAIL_CONF["receiver_addrs"], mail.as_string() ) if __name__ == "__main__": send_csv_as_attachment()
注意:如果单个CSV文件体积超过服务器可用内存的1/3,建议改用分块流式读取的方式构造邮件内容,避免OOM。常规业务场景下MB级别的CSV文件用上述代码完全可以稳定运行,不会产生任何本地临时文件,符合你们公司的安全要求。
内容的提问来源于stack exchange,提问作者Stan Ostrovskii
相关产品推荐
相关产品推荐

