blastengine APIを使ってJavaで添付ファイル付きメールを送信する

[blastengine](https://blastengine.jp/)はシンプルに使える開発者向けメールサービスを提供しています。何かと面倒なメール配信をシンプルに、かつ確実に送信先に届くようになります。

今回はblastengineのAPIを使ってJavaで添付ファイル付きのメール送信を行うまでの流れを解説します。

## ユーザ登録する

blastengineにユーザ登録します。管理画面に入るためのユーザID、パスワードが手に入るので、ログインします（ユーザIDは後で使います）。

![getting-started-4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/515541ad-b74e-e1e2-ac34-46d181ee2924.jpeg)


## 送信元ドメインのSPFを設定する

送信元として利用するドメイン（自分で持っているもの）の設定をします。これは任意のドメイン管理サービスで設定できますが、TXTレコードに以下のSPFを追加します。

```
txt @ v=spf1 include:spf.besender.jp ~all
```

## API経由で配信する

まずはAPIを使ってHTMLメールを配信する流れを紹介します。

### APIキーを取得する

ログイン後、管理画面の右上にある設定メニューに移動します。

![getting-started-6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/47b2b2ac-2e41-77fe-d84f-0ae0b7541f36.jpeg)


そして設定の中で、APIキーを取得します。

![getting-started-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2b5ca124-3020-1d39-84e4-911cd4db27e2.png)


### クラスの準備

今回は `jp.blastengine` というパッケージで実装していきます。まずメール送信用のJSONオブジェクトの内容を格納するクラス `BEMail` を用意します。

```java
public class BEMail {
  public String subject;
	public String encode;
  public String text_part;
	public String html_part;
  public BEMailAddress from;
  public String to;
}
```

さらにメールアドレスを入れる `BEMailAddress` を用意します。

```java
public class BEMailAddress {
  public String name;
  public String email;
}
```

### パッケージのインポート

必要なパッケージをインポートします。

```java
package jp.blastengine;

// トークン生成用
import org.apache.commons.codec.digest.DigestUtils;
import java.util.Base64;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

// HTTPリクエスト用
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.HttpResponse;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.HttpEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.util.EntityUtils;
import org.apache.http.entity.StringEntity;
import java.io.IOException;

// JSON処理用
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

// 添付ファイル用
import java.io.*;
import java.nio.file.*;
import java.util.*;
```

### 必要な変数を準備

トークンを生成するのに必要なユーザ名とAPIキーを準備します。

```java
String username = "USERNAME";
String api_key = "APIKEY";
```

### トークンを生成する

APIリクエストするためのトークンを生成します。手順としては次のとおりです。

1. ユーザIDとAPIキーを連結する
2. SHA256のハッシュを生成する
3. ハッシュを全て小文字にする
4. 3の文字列をBASE64エンコードする

実際のコードで言うと、次のようになります。 `token` がトークンです。

```java
String digest = DigestUtils.sha256Hex(username + api_key);
String token = new String(Base64.getEncoder().encode(digest.toLowerCase().getBytes()));
```

### メールオブジェクトの作成

`BEMail` のインスタンスを作成して、必要な情報をセットします。fromのemailやtoなど、メールアドレスは利用されるものに書き換えてください。

```java
BEMail mail = new BEMail();
mail.subject = "テストメール";
mail.encode = "UTF-8";
mail.text_part = "テストメールの本文（テキスト）";
mail.html_part = "<h1>テストメールの本文（HTML）</h1>";
mail.to = "user@example.jp";
// Fromの作成
BEMailAddress fromAddress = new BEMailAddress();
fromAddress.name = "送信者サンプル";
fromAddress.email = "info@example.com";
// Fromをセット
mail.from = fromAddress;
```

そして `mail` をJSONにします。

```java
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(mail);
```

### 添付ファイルを準備する

添付ファイルを読み込んでバイト文字列にします。

```java
final Path path = Paths.get("../README.md");
final String fileName = path.getFileName().toString();
final byte[] bytes = Files.readAllBytes(path);
```

### MultipartEntityBuilderを作成

MultipartEntityBuilderを使って添付ファイルと、メール送信用のパラメータを準備します。

```java
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
// 添付ファイル
ContentBody attach = new ByteArrayBody(bytes, Files.probeContentType(path), fileName);
builder.addPart("file", attach);
// メール送信情報
ContentBody jsonEntity = new ByteArrayBody(json.getBytes(), ContentType.APPLICATION_JSON, "data");
builder.addPart("data", jsonEntity);
```

### APIリクエストする

では実際にメールを送信します。APIのエンドポイントは `https://app.engn.jp/api/v1/deliveries/transaction` になります。生成したトークンは `Authorization` ヘッダーに適用します。

```java
// HttpPostを作成
HttpPost httpPost = new HttpPost("https://app.engn.jp/api/v1/deliveries/transaction");
// トークンをセット
httpPost.setHeader("Authorization", "Bearer " + token);
// データをセット
HttpEntity httpEntity = builder.build();
httpPost.setEntity(httpEntity);
// HTTPクライアントを作成
HttpClient client = HttpClientBuilder.create().build();
// リクエストを実行
HttpResponse response = client.execute(httpPost);
```

### 結果

レスポンスを出力します。配信ID（delivery_id）は照会、変更および削除操作に必要です。

```java
System.out.println(EntityUtils.toString(response.getEntity()));
// { delivery_id: 9 }
```

### 全体のコード

今回のサンプルコードは次のようになります。実装時の参考にしてください。

```java
package jp.blastengine;

// トークン生成用
import org.apache.commons.codec.digest.DigestUtils;
import java.util.Base64;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

// HTTPリクエスト用
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.HttpResponse;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.HttpEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.util.EntityUtils;
import org.apache.http.entity.StringEntity;
import java.io.IOException;

// JSON処理用
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

// 添付ファイル用
import java.io.*;
import java.nio.file.*;
import java.util.*;

// トークン生成に必要な情報
String username = "USERNAME";
String api_key = "APIKEY";

// トークン生成
String digest = DigestUtils.sha256Hex(username + api_key);
String token = new String(Base64.getEncoder().encode(digest.toLowerCase().getBytes()));

BEMail mail = new BEMail();
mail.subject = "テストメール";
mail.encode = "UTF-8";
mail.text_part = "テストメールの本文（テキスト）";
mail.html_part = "<h1>テストメールの本文（HTML）</h1>";
mail.to = "user@example.jp";
// Fromの作成
BEMailAddress fromAddress = new BEMailAddress();
fromAddress.name = "送信者サンプル";
fromAddress.email = "info@example.com";
// Fromをセット
mail.from = fromAddress;

// 送信情報をJSON文字列化
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(mail);

// 添付ファイルを処理
final Path path = Paths.get("../README.md");
final String fileName = path.getFileName().toString();
final byte[] bytes = Files.readAllBytes(path);

// リクエストデータを作成
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
// 添付ファイル
ContentBody attach = new ByteArrayBody(bytes, Files.probeContentType(path), fileName);
builder.addPart("file", attach);
// メール送信情報
ContentBody jsonEntity = new ByteArrayBody(json.getBytes(), ContentType.APPLICATION_JSON, "data");
builder.addPart("data", jsonEntity);

// HttpPostを作成
HttpPost httpPost = new HttpPost("https://app.engn.jp/api/v1/deliveries/transaction");
// トークンをセット
httpPost.setHeader("Authorization", "Bearer " + token);
// データをセット
HttpEntity httpEntity = builder.build();
httpPost.setEntity(httpEntity);
// HTTPクライアントを作成
HttpClient client = HttpClientBuilder.create().build();
// リクエストを実行
HttpResponse response = client.execute(httpPost);

// 結果
System.out.println(EntityUtils.toString(response.getEntity()));
```

## まとめ

クラウドサービスではSMTPポートが塞がれている場合があるので、そうした時にはAPI経由を利用してください。SMTPリレーを使えば、より信頼性高く、安定した配信が実現できるでしょう。

APIとSMTPリレー、それぞれの要件に合わせて最適な方を選択してください。

[エンジニア向けメール配信システム「ブラストエンジン」](https://blastengine.jp/)