SendGridから携帯キャリア向けにメールを送信する方法

SendGridから携帯キャリア向けにメールを送信する方法

はじめに

「SendGridは携帯キャリアメールにも対応しているの?」よくこんなお問い合せをいただきます。

もちろん対応しています!
SendGridは米国発のサービスですが、日本のキャリアメールにも問題なくメールを届けることができます。でも、キャリアメールには様々な制限があるため、一般的なメールプロバイダに送信するのと比べて、気をつけなければいけないことが増えてしまうのも事実です。そこで今回は、キャリアメール宛てにメールを送る際に気をつけるべきポイントと、Javaプログラムから送信するための具体的な方法をご紹介します。

前提条件

今回、動作確認に使用した環境は以下の通りです。

  • ドコモ:SH-02K、SO-51A(Android 10)
  • au:W62K
  • ソフトバンク:202SH

サンプルコードを試す前に以下の環境のセットアップを完了しておいてください。

メールの形式(テキストメールとデコメ)

キャリアメールがサポートしているメールの形式には「テキストメール」の他、いわゆる「デコメ」があります。デコメはデコメールや、デコレメールなど各社で呼び名が異なります。

  • テキストメール
    テキストメールは本文がtext/plainで構成される最もシンプルなメールです。
  • デコメ
    デコメは本文がテキストパートとHTMLパートから構成されるマルチパートメールのことを指します。パートの構造は各社ごとに仕様が異なるため、キャリアごとに構造を変えて送る必要があります。また、画像を埋め込む方法もキャリアごとに異なります。

送信方法と文字コード

SendGridではWeb APISMTPマーケティングキャンペーン機能の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を参照してください。依存ライブラリは以下の通りです。

詳しくはリポジトリ内の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を使って国内携帯キャリア対応のデコメールを送る」の内容を参考にさせていただきました。ありがとうございました。

参考記事

メールを成功の原動力に

開発者にもマーケターにも信頼されているメールサービスを利用して、
時間の節約、スケーラビリティ、メール配信に関する専門知識を手に入れましょう。