SendGridから携帯キャリア向けにメールを送信する方法
- 2020年11月13日
- by SendGrid
- Category: 技術ネタ
はじめに
「SendGridは携帯キャリアメールにも対応しているの?」よくこんなお問い合せをいただきます。
もちろん対応しています!
SendGridは米国発のサービスですが、日本のキャリアメールにも問題なくメールを届けることができます。でも、キャリアメールには様々な制限があるため、一般的なメールプロバイダに送信するのと比べて、気をつけなければいけないことが増えてしまうのも事実です。そこで今回は、キャリアメール宛てにメールを送る際に気をつけるべきポイントと、Javaプログラムから送信するための具体的な方法をご紹介します。
前提条件
今回、動作確認に使用した環境は以下の通りです。
- ドコモ:SH-02K、SO-51A(Android 10)
- au:W62K
- ソフトバンク:202SH
サンプルコードを試す前に以下の環境のセットアップを完了しておいてください。
- Java 1.8
- Gradle 6.7
- git
- SendGridアカウント
メールの形式(テキストメールとデコメ)
キャリアメールがサポートしているメールの形式には「テキストメール」の他、いわゆる「デコメ」があります。デコメはデコメールや、デコレメールなど各社で呼び名が異なります。
- テキストメール
テキストメールは本文がtext/plainで構成される最もシンプルなメールです。 - デコメ
デコメは本文がテキストパートとHTMLパートから構成されるマルチパートメールのことを指します。パートの構造は各社ごとに仕様が異なるため、キャリアごとに構造を変えて送る必要があります。また、画像を埋め込む方法もキャリアごとに異なります。
送信方法と文字コード
SendGridではWeb API、SMTP、マーケティングキャンペーン機能の3つの方法でメールを送信できます。Web APIとマーケティングキャンペーン機能は、簡単に送信することができますが、マルチパート構造を自由に変更できない制限があります。一方、SMTPはマルチパート構造を自由に指定できますが、その分設定が難しくなります。デコメのマルチパート構造に対応したメールを送るにはSMTPで送る必要があります。
また、文字コードについては、Web APIとマーケティングキャンペーン機能の場合、UTF-8のみ指定可能です。SMTPの場合、UTF-8、ISO-2022-JPなどが指定可能ですが、SendGrid側で自動的にUTF-8またはus-asciiに変換して送信します。
形式 | 送信方法 | 文字コード |
---|---|---|
テキストメール | SMTP | UTF-8、ISO-2022-JP、Shift_JISなどが指定可能だが、送信時に自動的にUTF-8またはus-asciiに変換される |
Web API | UTF-8のみ | |
マーケティングメール機能 | UTF-8のみ | |
デコメ | SMTP | UTF-8、ISO-2022-JP、Shift_JISなどが指定可能だが、送信時に自動的にUTF-8またはus-asciiに変換される |
文字化け
日本語メールを送る際、気になるのは文字コードと文字化けですが、今回確認した機種ではいずれも文字化けせずに届きました。
後述するサンプルコードで文字化けが発生する場合、受信側の携帯電話の制限の可能性があります。こちらを参考にして切り分けを行ってみてください。
サンプルコード
今回はJavaMailを使ってサンプルコードを実装しました。サンプルコードの実行方法はREADMEを参照してください。依存ライブラリは以下の通りです。
- JavaMail 1.6.2
- smtpapi-java 1.2.6
詳しくはリポジトリ内のbuild.gradleを参照してください。各ライブラリのより詳しい使い方についてはリファレンスを参照してください。
テキストメールを送信するサンプルコード
テキストメールを送信するサンプルコードは以下の通りです。宛名の差し込みとカテゴリの指定のみ行ったシンプルな内容です。複数の宛先を指定して一斉送信することもできます。
package com.github.sendgridjp; import java.io.UnsupportedEncodingException; import java.util.List; import java.util.Properties; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import javax.mail.internet.InternetAddress; import io.github.cdimascio.dotenv.Dotenv; import com.sendgrid.smtpapi.SMTPAPI; public class JavaMailTextExample { // 設定情報 static Dotenv dotenv = Dotenv.load(); static final String USERNAME = dotenv.get("SENDGRID_USERNAME"); static final String PASSWORD = dotenv.get("SENDGRID_PASSWORD"); static final String[] TOS = dotenv.get("TOS").split(","); static final String[] NAMES = dotenv.get("NAMES").split(","); static final String FROM = dotenv.get("FROM"); static final String SUBJECT = dotenv.get("SUBJECT"); static final String CHARSET = dotenv.get("CHARSET"); static final String ENCODE = dotenv.get("ENCODE"); public static void send() throws MessagingException, UnsupportedEncodingException { // SMTP接続情報 Properties props = new Properties(); props.put("mail.transport.protocol", "smtps"); props.put("mail.smtp.host", "smtp.sendgrid.net"); props.put("mail.smtp.port", "587"); props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.starttls.required", "true"); props.put("mail.smtp.auth", "true"); Authenticator auth = new SMTPAuthenticator(); Session mailSession = Session.getDefaultInstance(props, auth); mailSession.setDebug(true); // console log for debug // メッセージの構築 MimeMessage message = new MimeMessage(mailSession); // ダミーの宛先(X-SMTPAPIの宛先が優先される) message.addRecipient(Message.RecipientType.TO, new InternetAddress(FROM)); // From message.setFrom(FROM); // Subject message.setSubject(SUBJECT, CHARSET); // Body String body = "こんにちは、nameさん\r\nようこそ〜テキストメールの世界へ!"; message.setText(body, CHARSET, "plain"); message.setHeader("Content-Transfer-Encoding", ENCODE); // X-SMTPAPIヘッダ String smtpapi = createSmtpapi(TOS, NAMES); smtpapi = MimeUtility.encodeText(smtpapi); message.setHeader("X-SMTPAPI", MimeUtility.fold(76, smtpapi)); // 送信 mailSession.getTransport().send(message); } // X-SMTPAPIヘッダに設定する値の生成 private static String createSmtpapi(String[] tos, String[] names) { SMTPAPI smtpapi = new SMTPAPI(); smtpapi.setTos(tos); smtpapi.addSubstitutions("name", names); smtpapi.addCategory("category1"); return smtpapi.rawJsonString(); } private static class SMTPAuthenticator extends javax.mail.Authenticator { public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(USERNAME, PASSWORD); } } }
デコメを送信するサンプルコード
次はデコメを送信するサンプルコードです。キャリアに依存しないよう宛先ドメインごとにマルチパートの構造を切り替えています。
package com.github.sendgridjp; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.file.Path; import java.nio.file.FileSystems; import java.nio.file.Files; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.Multipart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimeUtility; import javax.mail.internet.InternetAddress; import javax.mail.util.ByteArrayDataSource; import io.github.cdimascio.dotenv.Dotenv; import com.sendgrid.smtpapi.SMTPAPI; public class JavaMailDecoExample { // 設定情報 static Dotenv dotenv = Dotenv.load(); static final String USERNAME = dotenv.get("SENDGRID_USERNAME"); static final String PASSWORD = dotenv.get("SENDGRID_PASSWORD"); static final String[] TOS = dotenv.get("TOS").split(","); static final String[] NAMES = dotenv.get("NAMES").split(","); static final String FROM = dotenv.get("FROM"); static final String SUBJECT = dotenv.get("SUBJECT"); static final String CHARSET = dotenv.get("CHARSET"); static final String ENCODE = dotenv.get("ENCODE"); public static void send() throws IOException, MessagingException, UnsupportedEncodingException { // SMTP接続情報 Properties props = new Properties(); props.put("mail.transport.protocol", "smtps"); props.put("mail.smtp.host", "smtp.sendgrid.net"); props.put("mail.smtp.port", "587"); props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.starttls.required", "true"); props.put("mail.smtp.auth", "true"); Authenticator auth = new SMTPAuthenticator(); Session mailSession = Session.getDefaultInstance(props, auth); mailSession.setDebug(true); // console log for debug // メッセージの構築 MimeMessage message = new MimeMessage(mailSession); // ダミーの宛先(X-SMTPAPIの宛先が優先される) message.addRecipient(Message.RecipientType.TO, new InternetAddress(FROM)); // From message.setFrom(FROM); // Subject message.setSubject(SUBJECT, CHARSET); // Body // Root part MimeMultipart rootPart = new MimeMultipart(); if (TOS[0].contains("@ezweb.ne.jp")) { rootPart.setSubType("mixed"); } else { rootPart.setSubType("related"); } // Alternative part MimeMultipart altPart = new MimeMultipart(); altPart.setSubType("alternative"); // Text part String body = "こんにちは、nameさん\r\nようこそ〜テキストメールの世界へ!"; MimeBodyPart textBodyPart = new MimeBodyPart(); textBodyPart.setText(body, CHARSET, "plain"); textBodyPart.setHeader("Content-Transfer-Encoding", ENCODE); altPart.addBodyPart(textBodyPart); // Html part String htmlBody = "<html>" + "<body bgcolor=\"#d9edf7\" style=\"background-color: #d9edf7;\">" + "こんにちは、nameさん<br>ようこそ〜デコメールの世界へ!<br>" + "<img src=\"cid:123@456\">" + "</body></html>"; MimeBodyPart htmlBodyPart = new MimeBodyPart(); htmlBodyPart.setText(htmlBody, CHARSET, "html"); htmlBodyPart.setHeader("Content-Transfer-Encoding", ENCODE); altPart.addBodyPart(htmlBodyPart); // Alternative part MimeBodyPart altBodyPart = new MimeBodyPart(); altBodyPart.setContent(altPart); rootPart.addBodyPart(altBodyPart, 0); // Attachment part MimeBodyPart attachmentPart = getMimeBodyPart( "./src/main/resources/SG_Twilio_Lockup_RGBx2_small.gif", "SG_Twilio_Lockup_RGBx2_small.gif", "image/gif", "123@456" ); rootPart.addBodyPart(attachmentPart); message.setContent(rootPart); // X-SMTPAPIヘッダ String smtpapi = createSmtpapi(TOS, NAMES); smtpapi = MimeUtility.encodeText(smtpapi); message.setHeader("X-SMTPAPI", MimeUtility.fold(76, smtpapi)); // 送信 mailSession.getTransport().send(message); } // FileからMimeBodyPartを生成 private static MimeBodyPart getMimeBodyPart( String path, String name, String type, String cid ) throws IOException, MessagingException, UnsupportedEncodingException { MimeBodyPart attachment = new MimeBodyPart(); byte[] bytes = Files.readAllBytes(FileSystems.getDefault().getPath(path)); DataSource dataSource = new ByteArrayDataSource(bytes, type); DataHandler dataHandler = new DataHandler(dataSource); attachment.setDataHandler(dataHandler); attachment.setFileName(MimeUtility.encodeWord(name)); attachment.setContentID("<" + cid + ">"); if (TOS[0].contains("@docomo.ne.jp")) { attachment.setDisposition(MimeBodyPart.INLINE); } else { attachment.setDisposition(MimeBodyPart.ATTACHMENT); } return attachment; } // X-SMTPAPIヘッダに設定する値の生成 private static String createSmtpapi(String[] tos, String[] names) { SMTPAPI smtpapi = new SMTPAPI(); smtpapi.setTos(tos); smtpapi.addSubstitutions("name", names); smtpapi.addCategory("category1"); return smtpapi.rawJsonString(); } // SMTP private static class SMTPAuthenticator extends javax.mail.Authenticator { public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(USERNAME, PASSWORD); } }
まとめ
今回は、SendGridを使って携帯キャリア向けにメールを届ける方法をご紹介しました。他にも、迷惑メールフィルタに関して気をつけるべきこととして、概要編となりすまし規制編の記事があるのでチェックしてみてください。
さいごに
今回ご紹介したコードのうち、マルチパート構造の切り替えと画像ファイルの埋め込み部分はクラスメソッド様のブログ記事「Amazon SESを使って国内携帯キャリア対応のデコメールを送る」の内容を参考にさせていただきました。ありがとうございました。