如何使用PIL /枕头作为电子邮件发送嵌入式图像(Python 3)

时间:2015-04-27 03:46:03

标签: python python-3.x pillow

我正在创建我想要嵌入电子邮件的图像。我无法弄清楚如何将图像创建为二进制并传递到MIMEImage。下面是我的代码,当我尝试读取图像对象时出错 - 错误是"属性错误:' NoneType'对象没有属性'读'"。

image=Image.new("RGBA",(300,400),(255,255,255))
image_base=ImageDraw.Draw(image)
emailed_password_pic=image_base.text((150,200),emailed_password,(0,0,0))
imgObj=emailed_password_pic.read()
msg=MIMEMultipart()
html="""<p>Please finish registration <br/><img src="cid:image.jpg"></p>"""
img_file='image.jpg'
msgText = MIMEText(html,'html')
msgImg=MIMEImage(imgObj)
msgImg.add_header('Content-ID',img_file)
msg.attach(msgImg)
msg.attach(msgText)

如果你看第4行 - 我试图读取图像,以便我可以将它传递给MIMEImage。显然,图像需要被读作二进制。但是,我不知道如何将其转换为二进制文件,以便.read()可以处理它。

后续 我根据jsbueno的建议编辑了代码 - 非常感谢!!!:

  emailed_password=os.urandom(16)
  image=Image.new("RGBA",(300,400),(255,255,255))
  image_base=ImageDraw.Draw(image)
  emailed_password_pic=image_base.text((150,200),emailed_password,(0,0,0))
  stream_bytes=BytesIO()
  image.save(stream_bytes,format='png')
  stream_bytes.seek(0)
  #in_memory_file=stream_bytes.getvalue()
  #imgObj=in_memory_file.read()
  imgObj=stream_bytes.read()
  msg=MIMEMultipart()
  sender='xxx@abc.com'
  receiver='jjjj@gmail.com'
  subject_header='Please use code provided in this e-mail to confirm your subscription.'
  msg["To"]=receiver
  msg["From"]=sender
  msg["Subject"]=subject_header
  html="""<p>Please finish registration by loging into your account and typing in code from this e-mail.<br/><img src="cid:image.png"></p>"""
  img_file='image.png'
  msgText=MIMEText(html,'html')
  msgImg=MIMEImage(imgObj) #Is mistake here?
  msgImg.add_header('Content-ID',img_file)
  msg.attach(msgImg)
  msg.attach(msgText)
  smtpObj=smtplib.SMTP('smtp.mandrillapp.com', 587)
  smtpObj.login(userName,userPassword)
  smtpObj.sendmail(sender,receiver,msg.as_string())

我现在没有收到错误,但电子邮件中没有图片。我对图像在html / email部分中的附加方式和相关方式感到困惑。任何帮助表示赞赏!

更新: 这段代码实际上有效 - 我的PC上的代码中只有一些小错误。

1 个答案:

答案 0 :(得分:4)

在使用PIL和图像的格式以便合并到电子邮件中时,存在一些概念上的错误。

在PIL中:ImageDraw类在本地操作,而不像Image类调用,它通常在每次操作后返回一个新图像。在您的代码中,这意味着对image_base.text的调用实际上正在更改image变量中对象的像素数据。这个调用实际上返回None,上面的代码应该引发一个错误,如“AttributeError:None对象没有属性'read'”在下一行。

过去(也就是说,你应该从你的image变量中获取数据以将其附加到电子邮件中)是第二个问题:出于显而易见的原因,PIL在未压缩的原始像素中有图像内存中的数据格式。在电子邮件中附加图像时,我们通常希望图像整齐地打包在文件中 - PNG或JPG格式通常更好,具体取决于意图 - 让我们留在.PNG。因此,您必须使用PIL创建文件数据,并且它们附加文件数据(即包括PNG文件的数据,包括标题,元数据和压缩形式的实际像素数据)。否则你会在你的电子邮件中放入一堆(未压缩的)像素数据,接收方无法将其组合回图像(即使他将数据视为像素,原始像素数据不包含图像形状如此 - )

您必须选择:在内存中生成文件字节,或将它们写入磁盘中的实际文件,然后重新读取该文件以进行附加。第二种形式更容易理解。第一个是更有效率和“正确的事情” - 所以让我们保持它:

from io import BytesIO
# In Python 2.x:
# from StringIO import StringIO.StringIO as BytesIO

image=Image.new("RGBA",(300,400),(255,255,255))
image_base=ImageDraw.Draw(image)
# this actually modifies "image"
emailed_password_pic=image_base.text((150,200),emailed_password,(0,0,0))
stream = BytesIO()
image.save(stream, format="png")
stream.seek(0)
imgObj=stream.read()
...

(注意:我没有在你的代码中检查处理邮件和mime的部分 - 如果你正确使用它,它现在应该工作了)