Flask部署服务器后SMTP发送邮件报400请求错误求助
Flask部署后SMTP发送带Zip附件邮件报400错误的排查方案
先修复代码中的致命错误
你的线程创建方式存在逻辑错误:
# 错误写法:直接调用函数,会在主线程同步执行而非子线程 thread = threading.Thread(target=send_mail(recipient_mail,filename)) # 正确写法:传入函数对象+参数 thread = threading.Thread(target=send_mail, args=(recipient_mail, filename))
原写法会导致send_mail在请求线程中立即执行,服务器请求结束后可能销毁应用上下文,引发后续操作失败。
分步排查与修复
1. 添加错误日志,定位具体问题
服务器环境无法直观看到报错细节,必须捕获并记录异常:
import logging logging.basicConfig(filename='email_errors.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s') def send_mail(recipient_mail,filename): with app.app_context(): try: # 原函数所有业务代码放在这里 except Exception as e: logging.error(f"发送邮件失败: {str(e)}", exc_info=True) return False
查看email_errors.log,可明确是文件操作失败、SMTP登录失败还是邮件格式问题。
2. 检查服务器文件权限与路径
- 确认Flask运行用户(如
www-data)对/home/mechatt.tk/converted/目录有读权限,对临时文件生成目录有读写权限 - 用绝对路径替代相对路径,避免因服务器工作目录差异导致文件找不到:
import os import uuid # 获取当前脚本所在目录 base_dir = os.path.dirname(os.path.abspath(__file__)) # 生成唯一临时目录/文件名,避免多线程冲突 temp_dir = os.path.join(base_dir, f"temp_files_{uuid.uuid4().hex}") zip_file_path = os.path.join(base_dir, f"files_{uuid.uuid4().hex}.zip")
3. 验证Gmail SMTP配置
Gmail的SMTP安全限制严格,服务器环境易踩坑:
- 必须使用应用专用密码:开启Google账户两步验证后,在账户安全设置中生成应用专用密码,替换原密码
- 检查服务器出站端口:部分服务商封禁587端口,改用465端口(SSL)尝试:
# 替换原SMTP连接代码 with smtplib.SMTP_SSL(Constants.SMTP_SERVER, 465) as server: server.login(Constants.SENDER_EMAIL, Constants.SENDER_PASSWORD) server.sendmail(Constants.SENDER_EMAIL, recipient_mail, message.as_string()) - 确认Gmail账户未限制第三方应用访问:检查账户是否因异常登录保护拦截了SMTP请求
4. 规范邮件格式,避免编码问题
服务器环境字符编码可能与本地不同,优化邮件头编码:
from email.header import Header # 替换主题设置,确保编码正确 message['Subject'] = Header('Zip文件包含请求内容', 'utf-8') # 提前校验收件人邮箱格式 if not recipient_mail or '@' not in recipient_mail: logging.error(f"无效收件人邮箱: {recipient_mail}") return
5. 优化临时文件清理逻辑
原代码仅在发送成功后清理文件,失败时会残留文件,建议用try-finally确保清理:
def send_mail(recipient_mail,filename): with app.app_context(): temp_dir = None zip_file_path = None try: # 创建临时目录、生成zip等业务逻辑 temp_dir = os.path.join(base_dir, f"temp_files_{uuid.uuid4().hex}") zip_file_path = os.path.join(base_dir, f"files_{uuid.uuid4().hex}.zip") # ... 其余代码 except Exception as e: logging.error(f"发送邮件失败: {str(e)}", exc_info=True) finally: # 无论成功失败都清理临时文件 if zip_file_path and os.path.exists(zip_file_path): os.remove(zip_file_path) if temp_dir and os.path.exists(temp_dir): shutil.rmtree(temp_dir)
内容的提问来源于stack exchange,提问作者Ashmit Singh
相关产品推荐
相关产品推荐

