带有 html 的电子邮件附件在某些设备上不可见
Posted
技术标签:
【中文标题】带有 html 的电子邮件附件在某些设备上不可见【英文标题】:Email attachments with html not visible on some devices 【发布时间】:2016-01-24 08:03:57 【问题描述】:我想发送一封带有 content-ID 附件(直接在消息中的图像)的 html 电子邮件,并且 Content-Disposition 附件是一个 xls 文件。
根据我使用的客户端邮件,xls 文件不可见(例如在 iPhone、iPad 或三星上)。
我想我没有在邮件的正确部分包含 xls 文件,但是当我尝试在 MIMEMultipart('alternative')
之前包含它时,它不起作用。
有人知道以下代码中问题的根源吗?
import base64
import httplib2
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import mimetypes
from email.mime.image import MIMEImage
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.application import MIMEApplication
from apiclient.discovery import build
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import run
html = """\
<html>
<head></head>
<body>
<p>Hi!<br>
XXX <br><br>
Hereunder a short overview of market evolution :<br>
<img src="cid:image1" >
</p>
</body>
</html>
"""
to='test@gmail.com';
subject='XXX';
message_main = html;
attachment_paths = [('test.png'), '<image1>'),
('2910e0f.xls', False)]
# Check https://developers.google.com/gmail/api/auth/scopes for all available scopes
OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.compose'
# Location of the gmail.storage
STORAGE = Storage('gmail.storage')
# Start the OAuth flow to retrieve credentials
flow = flow_from_clientsecrets(CLIENT_SECRET_FILE, scope=OAUTH_SCOPE)
http = httplib2.Http()
# Try to retrieve credentials from storage or run the flow to generate them
credentials = STORAGE.get()
if credentials is None or credentials.invalid:
credentials = run(flow, STORAGE, http=http)
# Authorize the httplib2.Http object with our credentials
http = credentials.authorize(http)
## Save credentials to storage
# STORAGE.put(credentials)
# Build the Gmail service from discovery
gmail_service = build('gmail', 'v1', http=http)
message = MIMEMultipart('related') # 'alternative', 'multipart', 'mixed', related
message['to'] = to
message['from'] = "YYY@gmail.com"
message['subject'] = subject
message.preamble = 'This is a multi-part message in MIME format.'
# Encapsulate the plain and HTML versions of the message body in an
# 'alternative' part, so message agents can decide which they want to display.
msgAlternative = MIMEMultipart('alternative')
message.attach(msgAlternative)
msg1 = MIMEText(message_main, 'plain')
msg2 = MIMEText(message_main, 'html')
msgAlternative.attach(msg1)
msgAlternative.attach(msg2)
for filename, link in attachment_paths:
print '----'
print filename
print link
content_type, encoding = mimetypes.guess_type(filename)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
if main_type == 'text':
fp = open(filename, 'rb')
msg = MIMEText(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'image':
fp = open(filename, 'rb')
msg = MIMEImage(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'audio':
fp = open(filename, 'rb')
msg = MIMEAudio(fp.read(), _subtype=sub_type)
fp.close()
else:
fp = open(filename, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
if not link:
# msg.add_header('Content-Disposition', 'attachment', filename=filename)
# message.attach(msg)
# pdf = open(filename, 'rb').read()
# msgPdf = MIMEApplication(pdf, 'pdf') # pdf for exemple
# msgPdf.add_header('Content-ID', '<pdf1>') # if no cid, client like MAil.app (only one?) don't show the attachment
msg.add_header('Content-Disposition', 'attachment', filename=filename)
# msg.add_header('Content-Disposition', 'inline', filename=filename)
else:
# Define the image's ID as referenced above
msg.add_header('Content-ID', link)
msg.add_header('Content-Disposition', 'attachment', filename=filename)
message.attach(msg)
body = 'raw': base64.urlsafe_b64encode(message.as_string())
# body = 'raw': base64.b64encode(message.as_string())
# send it
try:
message = (gmail_service.users().messages().send(userId="me", body=body).execute())
print('Message Id: %s' % message['id'])
print(message)
except Exception as error:
print('An error occurred: %s' % error)
【问题讨论】:
【参考方案1】:解决方案包括将初始多部分容器更改为混合并在两步过程中附加文件:
简单附件的MIMEMultipart('alternative')
声明之前
在MIMEMultipart('alternative')
声明后与html正文相关的附件(主要是邮件内的图片)
您可以在下面找到正确的代码:
import base64
import httplib2
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import mimetypes
from email.mime.image import MIMEImage
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.application import MIMEApplication
from apiclient.discovery import build
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import run
html = """\
<html>
<head></head>
<body>
<p>Hi!<br>
XXX <br><br>
Hereunder a short overview of market evolution :<br>
<img src="cid:image1" >
</p>
</body>
</html>
"""
to='test@gmail.com';
subject='XXX';
message_main = html;
attachment_paths_html = [('test.png'), '<image1>')]
attachment_paths_mixed = [('2910e0f.xls', '2910e0f.xls')]
# Check https://developers.google.com/gmail/api/auth/scopes for all available scopes
OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.compose'
# Location of the gmail.storage
STORAGE = Storage('gmail.storage')
# Start the OAuth flow to retrieve credentials
flow = flow_from_clientsecrets(CLIENT_SECRET_FILE, scope=OAUTH_SCOPE)
http = httplib2.Http()
# Try to retrieve credentials from storage or run the flow to generate them
credentials = STORAGE.get()
if credentials is None or credentials.invalid:
credentials = run(flow, STORAGE, http=http)
# Authorize the httplib2.Http object with our credentials
http = credentials.authorize(http)
## Save credentials to storage
# STORAGE.put(credentials)
# Build the Gmail service from discovery
gmail_service = build('gmail', 'v1', http=http)
message = MIMEMultipart('mixed')
message['to'] = to
message['from'] = "YYY@gmail.com"
message['subject'] = subject
message.preamble = 'This is a multi-part message in MIME format.'
for filename, name in attachment_paths_mixed:
print '----'
print filename
print name
content_type, encoding = mimetypes.guess_type(filename)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
if main_type == 'text':
fp = open(filename, 'rb')
msg = MIMEText(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'image':
fp = open(filename, 'rb')
msg = MIMEImage(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'audio':
fp = open(filename, 'rb')
msg = MIMEAudio(fp.read(), _subtype=sub_type)
fp.close()
else:
fp = open(filename, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
msg.add_header('Content-Disposition', 'attachment', filename=name)
message.attach(msg)
# Encapsulate the plain and HTML versions of the message body in an
# 'alternative' part, so message agents can decide which they want to display.
msgAlternative = MIMEMultipart('alternative')
message.attach(msgAlternative)
msg1 = MIMEText(message_main, 'plain')
msg2 = MIMEText(message_main, 'html')
msgAlternative.attach(msg1)
msgAlternative.attach(msg2)
for filename, link in attachment_paths_html:
print '----'
print filename
print link
content_type, encoding = mimetypes.guess_type(filename)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
if main_type == 'text':
fp = open(filename, 'rb')
msg = MIMEText(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'image':
fp = open(filename, 'rb')
msg = MIMEImage(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'audio':
fp = open(filename, 'rb')
msg = MIMEAudio(fp.read(), _subtype=sub_type)
fp.close()
else:
fp = open(filename, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
# Define the image's ID as referenced above
msg.add_header('Content-ID', link)
message.attach(msg)
body = 'raw': base64.urlsafe_b64encode(message.as_string())
# body = 'raw': base64.b64encode(message.as_string())
# send it
try:
message = (gmail_service.users().messages().send(userId="me", body=body).execute())
print('Message Id: %s' % message['id'])
print(message)
except Exception as error:
print('An error occurred: %s' % error)
【讨论】:
以上是关于带有 html 的电子邮件附件在某些设备上不可见的主要内容,如果未能解决你的问题,请参考以下文章
使用 HTML 编码时,为啥视频在某些 Android 设备上加载,而在其他设备上不加载?