AWS Glue Python作业发送带S3附件邮件失败问题排查
AWS Glue作业发送带S3附件邮件时遇到SMTP 554错误
我在使用AWS Glue作业发送包含S3桶中多个附件的邮件时遇到了问题。代码执行到server.login()都正常,但调用server.sendmail()时失败,错误信息如下:
Error: (554, b'Transaction failed: Expected '=', got "null"')
相关代码片段如下:
def sendEmail(TO, SUBJECT, BODY_HTML): msg = MIMEMultipart('alternative') msg['Subject'] = SUBJECT msg['From'] = SENDER msg['To'] = ','.join(RECIPIENT + TO) part1 = MIMEText(BODY_HTML, 'html') msg.attach(part1) s3 = boto3.resource('s3') bucket = s3.Bucket('sample-bucket') for obj in bucket.objects.filter(Delimiter='/', Prefix='sample-folder/'): filename = ((obj.key).split("/")[1]) s3_object = s3_obj.s3_get_object(sample-bucket, 'sample-folder/'+ filename) body = s3_object['Body'].read() part = MIMEApplication(body, filename) part.add_header("Content-Disposition", 'attachment', filename=filename) msg.attach(part) try: server = smtplib.SMTP(HOST, PORT) server.ehlo() server.starttls() server.ehlo() server.login(USERNAME_SMTP, PASSWORD_SMTP) server.sendmail(SENDER, RECIPIENT, msg.as_string()) # --Error-- server.close() print (msg) print ("Email sent")
问题原因及修复方案
最可能导致这个SMTP 554错误的原因是邮件头To字段格式无效,同时代码中还有几处S3对象处理的问题,下面逐一说明:
收件人列表拼接错误
代码中msg['To'] = ','.join(RECIPIENT + TO)存在逻辑漏洞:- 如果
RECIPIENT是单个字符串(比如'a@example.com'),RECIPIENT + TO会把字符串拆成字符列表再和TO列表拼接,最终','.join会生成类似a,@,e,x,a,m,p,l,e,.,c,o,m,b@example.com的无效格式,这会导致邮件服务器解析邮件头时出错,触发554错误。 - 同时
server.sendmail()的第二个参数需要是收件人列表(而非单个字符串),如果RECIPIENT是单个邮箱,应该把它转为列表再和TO合并。
修复方式:
# 确保收件人是列表格式 recipients = [RECIPIENT] + TO if isinstance(RECIPIENT, str) else RECIPIENT + TO msg['To'] = ','.join(recipients) # sendmail的第二个参数传入收件人列表 server.sendmail(SENDER, recipients, msg.as_string())- 如果
S3对象获取代码错误
- 代码中
s3_obj未定义,你之前初始化的是s3 = boto3.resource('s3'),应该用资源对象的方法来获取文件; sample-bucket没有加引号,属于语法错误,需要用字符串包裹;- 当
obj.key是sample-folder/(前缀本身)时,split("/")[1]会得到空字符串,导致文件名无效,需要跳过前缀目录本身。
修复后的S3处理代码:
s3 = boto3.resource('s3') bucket = s3.Bucket('sample-bucket') for obj in bucket.objects.filter(Prefix='sample-folder/'): # 跳过前缀目录本身(如果存在) if obj.key == 'sample-folder/': continue # 从key中提取文件名 filename = obj.key.split("/")[-1] # 获取S3对象内容 s3_object = s3.Object('sample-bucket', obj.key).get() body = s3_object['Body'].read() part = MIMEApplication(body, Name=filename) part.add_header("Content-Disposition", 'attachment', filename=filename) msg.attach(part)- 代码中
MIMEApplication初始化参数问题
MIMEApplication(body, filename)的第二个参数是_subtype,不是文件名,正确的做法是用Name参数指定文件名,如上面修复代码所示,这样能确保附件的MIME头更规范。
总结
优先修复收件人列表的拼接错误,这是触发554错误的核心原因。同时修正S3对象处理的语法和逻辑问题,避免后续出现其他异常。
内容的提问来源于stack exchange,提问作者TeeKay
相关产品推荐
相关产品推荐

