本地正常服务器报错TypeError: Could not guess image MIME subtype求助
TypeError: Could not guess image MIME subtype 问题场景
使用Gramex capture API捕获图表生成PNG图片,通过<img>标签嵌入邮件模板,本地环境邮件发送正常,但服务器端触发以下错误:
TypeError: Could not guess image MIME subtype
已确认服务器上图片为PNG格式且路径正确,核心业务代码如下:
def send_email(handler): user = handler.args.get('user', ['None'])[0] chart_selector = ['.bar-chart', '.donut-chart', '.world_map', '.headerImg'] folder = os.path.dirname(os.path.abspath(__file__)) mailer = service.email["galaxy-email-smtp"] try: yield service.threadpool.submit( load_file, user, chart_selector ) except Exception as e: print("An error occurred:", str(e)) temp_file = gc.open('email.digest.template.formatted.html', 'template') file_path_barchart = os.path.join(folder, 'downloaded1.png') file_path_donutchart = os.path.join(folder, 'downloaded2.png') file_path_worldmap = os.path.join(folder, 'downloaded3.png') file_path_header = os.path.join(folder, 'downloaded4.png') html_content = temp_file.generate(data={"handler": handler}).decode('utf-8') mailer.mail( sender=<email>, to=user, subject=<subject>, html=html_content, images={ "headerImg": file_path_header, "ricoImg": os.path.join(folder, 'assets/img/rico.png'), "bar-chart-img": file_path_barchart, "donut-chart-img": file_path_donutchart, "world_map": file_path_worldmap, }, ) return "email has been sent" def load_file(user, chart_selector): url = <url> + user for index, each_chart_selector in enumerate(chart_selector): file_name = f"downloaded{index+1}.png" # Generate the file name chart_capture = capture.png( url, selector=each_chart_selector, scale=2, timeout=60 ) with open(file_name, "wb") as f: f.write(chart_capture)
错误栈指向Gramex的emailer.py中创建MIMEImage时未指定_subtype参数:
for name, path in images.items(): with open(path, 'rb') as handle: img = MIMEImage(handle.read()) img.add_header('Content-ID', f'<{name}>') html_part.attach(img)
手动修改为img = MIMEImage(handle.read(), 'png')后问题解决,询问是否有官方配置方式指定图片subtype。
原因分析
Python的email.mime.image.MIMEImage默认会尝试通过图片内容自动推断MIME子类型,但服务器环境可能缺少文件类型识别依赖(如python-magic),或者捕获的PNG图片头部信息不完整,导致无法自动识别,从而触发该错误。本地环境通常依赖完整,所以能正常识别。
解决方案
目前Gramex官方邮件服务的images参数仅支持传入文件路径,没有直接配置MIME子类型的选项。可以通过以下两种方式解决:
1. 临时修改Gramex源码(快速解决)
如你已经尝试的,修改gramex/services/emailer.py中创建MIMEImage的代码,指定_subtype='png':
img = MIMEImage(handle.read(), _subtype='png')
注意:这种方式会影响所有通过该Gramex实例发送的图片邮件,若有其他格式图片需要发送,需额外判断文件扩展名来动态指定subtype。
2. 自定义邮件构建逻辑(更灵活)
绕过Gramex邮件服务的images参数,手动构建MIME邮件并嵌入图片,精确控制每个图片的subtype:
from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage def send_email(handler): # ... 原有代码逻辑 ... html_content = temp_file.generate(data={"handler": handler}).decode('utf-8') # 手动构建多部分邮件 msg = MIMEMultipart('related') msg['From'] = <email> msg['To'] = user msg['Subject'] = <subject> # 添加HTML部分 html_part = MIMEText(html_content, 'html', 'utf-8') msg.attach(html_part) # 手动嵌入图片,指定subtype images = { "headerImg": (file_path_header, 'png'), "ricoImg": (os.path.join(folder, 'assets/img/rico.png'), 'png'), "bar-chart-img": (file_path_barchart, 'png'), "donut-chart-img": (file_path_donutchart, 'png'), "world_map": (file_path_worldmap, 'png'), } for cid, (path, subtype) in images.items(): with open(path, 'rb') as f: img = MIMEImage(f.read(), _subtype=subtype) img.add_header('Content-ID', f'<{cid}>') img.add_header('Content-Disposition', 'inline', filename=os.path.basename(path)) msg.attach(img) # 使用Gramex邮件服务发送自定义构建的邮件 mailer.send(msg) return "email has been sent"
3. 提交Feature Request
可以在Gramex的官方代码仓库提交Feature Request,建议为mail()方法的images参数支持传入包含路径和subtype的元组,或者添加额外参数指定默认subtype,无需修改源码即可解决问题。
内容的提问来源于stack exchange,提问作者Lahari

