GMAILで画像付きHTMLメール結構面倒になっていた、回避策忘備録
投稿している今日は、2024年12月29日もうすぐ今年も終わりです。
子供のころは手作りの年賀はがきを作っておくりぬくもりがあって楽しかったし、社会人になると逆に儀礼的な感じであまり心のやり取りなくただ面倒な労力だけであった記憶があります。
でも現在は少なくとも儀礼的なものはコストも労力のないメールで一斉に送ることが多いのではないでしょうか?
そんななかでも中に会社のロゴをはじめいろいろな画像で印象的なデザインで広告宣伝の活動としても重要なものになっているかもしれません。
総務省の「令和5年版 情報通信白書」によると、Gmailの利用率は65.2%だそうです。
その上でHTMLメールをGMAILに送ったときに起きた問題の解決したときの防備メモを書いておこうともいます。
メールをHTML形式でおくるとき画像などはまずUTLリンクで埋め込むことを考える方が多いのではないでしょうか?
一番簡単でIMGタグのSRCにURLリンクを設定することです。
<img src="https://www.mydomain.com/images/1.png" style="display: block; height: auto; border: 0; width: 100%;" width="600">
これはHTMLのサイズ自体も小さくなりますし。
でも、最初これがまったくGMAIL系のメールには画像が表示されなくなりました。
MS系のメールには問題なく表示されているにもかかわらずですが。
リンクアドレスをチェックすると
https://ci3.googleusercontent.com/meips/ADKq_NbfMDwAtzUED3jM6ZFsSJTus6dktbgXA4l7soxD6-5QN1CD4Re9fklVkq8jnhPacrHlC2svVbj6o6fFLdtesQ=s0-d-e1-ft#https://www.mydomain.com/images/1.png
調べたところ、GOOGLEは現在GOOGLE側で画像URLアドレスにアクセスをして画像データをキャシュするそうです。つまりPROXYのような設定ですね。つまり画像がインターネット上に無制限に公開されていれば基本的にこの方法で問題ないのですが、画像を特定のIPやGEOIPなど国制限していると機能しなくなったことが原因のようでした。私の場合はGEOIPの許可国にUSも入れて回避しましたがGOOGLEはあらゆる国にサーバーを運用してますのでUSだけを許可してもいずれ機能しなくなるかもしれません。またGEMAIL系のサービスに限らずメーラーによっては受け取った後画像を再度ロードしないといけない場合もあります。(プライバシーというかセキュリティ対策でトラッキングを防ぐためだと思いますが)
画像の入ったWEBサーバーをアクセス元IP制限で限定公開したいときとか、認証しないとアクセスできないところにおいてある場合これは頭が痛いところです。
そこでイメージをURLでなくBASE64にすることで解決しようとしました。
これはURLの代わりにbase64にエンコードしてIMGタグのSRCに設定する方法です
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg.....>
こうして、HTMLを送信、MS系のメールには特に問題なく埋め込まれたBASE64の画像が表示されました
ところがGMAIL系のメールではBASE64の文字列がそのままだらだらとでてしまいました!
どうやらまたセキュリティの制限のようです。
ここでやっと解決策ですが画像を添付方式でMIMEに入れて送る方法をとりました。
いきなり解説無しでコード貼り付けてしまいますが、以下のようにして、IMGタグがBASE64のときはcidを付与して中身をMIMEで添付するような方式で実施してます。
import smtplib
import re
import base64
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.utils import formataddr
def process_base64_images(html_content):
"""Base64画像をCID形式に変換し、インライン画像として添付する準備を行う"""
inline_images = {}
# Base64画像タグを検索
base64_image_tags = re.findall(r'<img src="data:image/(png|jpeg|gif);base64,([^\"]+)"[^>]*>', html_content)
for index, (format, base64_data) in enumerate(base64_image_tags):
# Base64データをバイナリデータにデコード
image_data = base64.b64decode(base64_data)
# 一意のIDを生成してcidに使用
image_id = f"image_{index}"
# cid形式のタグに置き換える
html_content = html_content.replace(
f'data:image/{format};base64,{base64_data}', f'cid:{image_id}'
)
# MIMEImageオブジェクトを生成
image_mime = MIMEImage(image_data, _subtype=format)
image_mime.add_header('Content-ID', f'<{image_id}>')
inline_images[image_id] = image_mime
return html_content, inline_images
def send_email(to_name, to_email, html_template):
# AWS SESの設定
SMTP_SERVER = "MY_SMTP_SERVER" #SMTPサーバ
SMTP_PORT = 587 #SMTP PORT
SMTP_USERNAME = "MY_SMTP_ID"
SMTP_PASSWORD = "MY_SMTP_PASSWD"
# HTMLテンプレートの名前置き換え
personalized_html = html_template.replace("{To_Name}", to_name)
# Base64画像を処理
personalized_html, inline_images = process_base64_images(personalized_html)
# メール設定
sender_name = "タケ フューチャー"
sender_email = "mymirai@futurefuture.com"
msg = MIMEMultipart()
msg['From'] = formataddr((sender_name, sender_email))
msg['To'] = to_email
msg['Subject'] = "こんちわ! 元気?"
# メール内容
msg.attach(MIMEText(personalized_html, 'html'))
# インライン画像を添付
for image_mime in inline_images.values():
msg.attach(image_mime)
try:
# SMTP接続
server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
server.starttls()
server.login(SMTP_USERNAME, SMTP_PASSWORD)
server.sendmail(sender_email, to_email, msg.as_string())
print(f"{to_email}にメールを送信しました")
except Exception as e:
print(f"エラーが発生しました: {e}")
if __name__ == "__main__":
html_template_file = "mailtemplate.html"
with open(html_template_file, 'r', encoding='utf-8') as file:
html_template = file.read()
send_email('はちさん', 'bigdog8@dogdog.com', html_template)
こまかい解説は割愛しますが、簡単にいうと、BASE64の画像データの中身を読んでそのデータにCIDを割り当てる。HTMLにはCIDをリンクとして書いておき、画像データとCIDの対応辞書inline_imagesを再度読み込んで要素をメールに添付する。HTMLはcidのリンクとして添付ファイルにリンクされるという理解です。
以上です、それでは皆様よい年末年始を!
Discussion