JavaでGmail APIを使ってメールを取得する

#はじめに
GmailのAPIをJavaで使ってみたけど、使うにあたってググっても、出てくるのがStack OverflowとかGoogleの公式サイトの英語の記事ばかりで悲しかったので、俺が日本語の記事を書かねばと思った。

#やりたいこと
Gmail上で受信したメールをJavaを使って本文と添付ファイルを取得したい。

#前提

- Eclipseつかいます。
- Googleのアカウントを用意しておきましょう。

#取得したいメール
今回で取得するためのサンプルのメールです。
![19.png](https://qiita-image-store.s3.amazonaws.com/0/257548/feeaa437-1e92-438e-2212-3a651a054e03.png)
添付ファイルでLinuxのペンギンの画像をつけておきました。


#ライブラリの取り込み
まずはGmailのAPIを使う準備として、以下のURLからGmail APIのライブラリを取得する。
[Gmail API Client Library for Java ; https://developers.google.com/api-client-library/java/apis/gmail/v1](https://developers.google.com/api-client-library/java/apis/gmail/v1 "Gmail API Client Library for Java")

MAVENとGRADLEを使っている人はリンク先の指示にしたがってライブラリをインストールできる。
諸般の理由でそういった便利なものを使えない人は、zipでダウンロードして中のjarファイルの全部を取り込みましょう。

#認証の設定
以下の2つをやる。

- Googleアカウント上でプロジェクトを作成する。
- Googleアカウント上でGmail APIを有効化する。
- Gmail APIは認証でOAuth 2.0をつかうので、Googleアカウント上でOAuth 2.0 クライアントを用意する。

##プロジェクトを作成
以下のURLにアクセスしてプロジェクトをつくる。
[リソースの管理 ; https://console.developers.google.com/cloud-resource-manager](https://console.developers.google.com/cloud-resource-manager)
__1. 「プロジェクトを作成」をクリック__
![13.png](https://qiita-image-store.s3.amazonaws.com/0/257548/e9de22b3-885c-f724-235e-8c485b3d2977.png)

__2. 適当にプロジェクト名をつけて「作成」をクリック__
![14.png](https://qiita-image-store.s3.amazonaws.com/0/257548/f090389d-fc42-48ab-8f34-b6c3059b6792.png)

__3. しばらくするとプロジェクトができる__
少し待ってよう
![15.png](https://qiita-image-store.s3.amazonaws.com/0/257548/42547c6c-f802-4c16-ada1-ccdce1a33c98.png)

##Gmail APIを有効化 
以下のURLからGoogleのAPIライブラリにアクセスしてGmail APIを有効化する。
[API ライブラリ ; https://console.developers.google.com/apis/library](https://console.developers.google.com/apis/library)
![6.png](https://qiita-image-store.s3.amazonaws.com/0/257548/c40efc1c-1a72-5f77-778c-394a37776afd.png)

##OAuth 2.0 クライアントを用意
以下のURLにアクセスをしてOAuth 2.0 クライアントをつくる。
[認証情報 ; https://console.developers.google.com/apis/credentials](https://console.developers.google.com/apis/credentials)

__1. 認証情報を作成をクリック__
![7.png](https://qiita-image-store.s3.amazonaws.com/0/257548/becab782-18b0-1372-dfde-b1298574585b.png)

__2. OAuthクライアントIDを選択__
![8.png](https://qiita-image-store.s3.amazonaws.com/0/257548/2a78f1d5-5f18-a042-3008-1a6899844d4f.png)

__3. 「その他」を選択して、適当に名前をつけて、「作成」をクリック__
![9.png](https://qiita-image-store.s3.amazonaws.com/0/257548/da753ab4-0836-79a4-b23f-bcaf0a27e736.png)

__4. 完成__
![11.png](https://qiita-image-store.s3.amazonaws.com/0/257548/9047fe87-c351-ecb2-5467-efe11d54e420.png)

__5. あとで使うので秘密鍵をダウンロードしておく__
なんでもいいけど「client_secret.json」って名前で保存しておきましょう。
![12.png](https://qiita-image-store.s3.amazonaws.com/0/257548/dfc8fe7e-bbc7-a9e8-7bbc-995a7400d6e6.png)


__6. OAuth同意画面も適当に作成__
![10.png](https://qiita-image-store.s3.amazonaws.com/0/257548/9c30ac08-bfe9-5bfc-5bbc-1369800f54ca.png)

#Javaを書いてみる
処理の流れは大まかに以下のような感じなので順にやっていく。

1. 認証
2. メッセージIDの取得
3. メールの取得
4. 本文のデコード
5. 添付ファイルのダウンロード

##認証
先で取得した秘密鍵を元に、APIを使うためのトークンと、トークンを再発行するためのリフレッシュトークンを取得する認証作業を行う。

__1. プロジェクト内に前で取得した秘密鍵の「client_secret.json」を配置する__
![16.png](https://qiita-image-store.s3.amazonaws.com/0/257548/bc640683-6c4a-f6b4-75a7-e7dd71d5bf52.png)

__2. 秘密鍵を元にリフレッシュトークンとかの認証に必要な情報を取得するのだが、それを保存するためのフォルダをプロジェクト内に作る__
とりあえず名前は「credentials」にしておく。
![17.png](https://qiita-image-store.s3.amazonaws.com/0/257548/3396ca30-0551-4f84-91a5-6ae5be5547b6.png)

__3. やっとコーディング__
秘密鍵を元に、APIを使うためのトークンと、トークンを再発行するためのリフレッシュトークンを取得する

```java
// Json処理
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
// スコープ
List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_READONLY);
// 秘密鍵
String CLIENT_SECRET_DIR = "client_secret.json";
// 認証周りの設定ファイルの格納フォルダ
String CREDENTIALS_FOLDER = "credentials";
// HTTP通信
NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

// 認証
// 秘密鍵の読み込み
FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
InputStreamReader reader = new InputStreamReader(in);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);
// 認証の設定
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
        clientSecrets, SCOPES)
                .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
                .setAccessType("offline").build();
// 認証を行う
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
```

まずは定数の説明

- __JSON_FACTORY__ : JSONを処理するためのオブジェクト。APIの返ってくるデータはJSON形式なので。
- __SCOPES__ : APIをつかってGmailにどこまでのアクセスを許可するか。GmailScopesクラスに定数で全部あるはずなのでそれから適切なものを選んでつかいましょう。今回やるようなことは__GMAIL_READONLY__を選んでおけば全部できるはずなので、それを選んでおきましょう。
- __CLIENT_SECRET_DIR__ : 秘密鍵のファイルのパス。今回は直下に「client_secret.json」という名前でいれたので、"client_secret.json"でOKです。
- __CREDENTIALS_FOLDER__ : 秘密鍵を使って取得した認証まわりのファイルを保存するフォルダのパス。上で作ったフォルダを設定。
- __HTTP_TRANSPORT__ : HTTP通信をするためのオブジェクト。

続いて処理の説明

```java
// 秘密鍵の読み込み
FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
InputStreamReader reader = new InputStreamReader(in);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);
```
直下にある秘密鍵の「client_secret.json」を読み込んでます


```java
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
    .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
    .setAccessType("offline").build();
```

認証を投げるための設定を入れてます。

- new GoogleAuthorizationCodeFlow.Builderの引数
    - __HTTP通信のオブジェクト__ : HTTP_TRANSPORT
    - __JSON処理のオブジェクト__ : JSON_FACTORY
    - __秘密鍵__ : clientSecrets
    - __スコープ__ : SCOPES（READ ONLYに設定したやつ）
- setDataStoreFactoryの引数
    - __秘密鍵を使って取得した認証まわりのファイルの出力先__
- setAccessTypeの引数
    - __オフラインアクセス__（人間がブラウザ上でするアクセスじゃないやつ）なので"offline"で

```java
// 認証を行う
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
```
実際にHTTP通信をして、秘密鍵を投げて、トークンとかアクセストークンとかを貰って、指定したフォルダに保存します。
実行すると以下のように、フォルダ内になんかファイルができているはずです。
![18.png](https://qiita-image-store.s3.amazonaws.com/0/257548/6b901566-a0f0-4928-74ca-22939cd79d4a.png)

##メッセージIDの取得
messagesのlistとかいうAPIつかいます。messagesはメールを取得、送信、操作したりする系のAPIで、listは文字通り受信したメールの一覧を取得します。ただの一覧なので、メッセージIDとスレッドIDしか取得できず、本文とかは見れません。本文とか詳細なデータを取得するためにはmessagesのgetを使いましょう。
とりあえず公式サイトの説明は以下（アメリカ語）

[Users.messages: list ; https://developers.google.com/gmail/api/v1/reference/users/messages/list](https://developers.google.com/gmail/api/v1/reference/users/messages/list)

APIを投げるのに必要な情報、必要なスコープ、返ってくるJSONの定義、各種言語でのサンプルコードとかが書いてあるので読めると便利です。アメリカ語だけど。

やることとしてはとりあえずGmailのサービスのオブジェクトを作って、そのオブジェクトでmessagesのlistを実行して、返ってきたデータをListにつっこみます。

```java
// Gmailのサービスを作成
// アプリケーション名
String APPLICATION_NAME = "Test1";
// Gmailにアクセス
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();

// メールの取得
// ユーザ
String USER = "me";
// メールを全て取得(メッセージIDしか入ってない)
ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
List<Message> messageList = messagesResponse.getMessages();
```
定数の説明

- __APPLICATION_NAME__ : 作成するGmailのサービスのオブジェクトにつける名前。適当でいい。

処理の説明

```java
// Gmailのサービスを作成
// アプリケーション名
String APPLICATION_NAME = "Test1";
// Gmailにアクセス
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();
```
Gmailのサービスのオブジェクトをつくってます。

- new Gmail.Builderの引数
    - __HTTP通信のオブジェクト__ : HTTP_TRANSPORT
    - __JSON処理のオブジェクト__ : JSON_FACTORY
    - __認証情報__ : credential
- setApplicationName
    - __作成するGmailのサービスの名前__

```java

// メールの取得
// ユーザ
String USER = "me";
// メールを全て取得(メッセージIDしか入ってない)
ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
List<Message> messageList = messagesResponse.getMessages();
```
messagesのlistを実行して、結果をListに格納しています。引数はユーザだけです。自分のメールを見たいので"me"をつっこんでおけば良いです。これで以下のような、メッセージIDとスレッドIDだけが入ったJSONのListができます。

```json
{
    "id": "1635e36ac0cf27ce",
    "threadId": "1635e36ac0cf27ce"
}
```

##メールの取得
メッセージIDから更に件名や本文などの詳細なデータを取得して、対象のメールを絞り込みます。

```java
// 対象のメールを見つけるためにメールの全データを探索する
Message targetMessage = null;
for (Message message : messageList) {
    // メールの全データを取得
    Message fullMessage = service.users().messages().get(USER, message.getId()).execute();
    // ヘッダーを見る
    for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
        // ヘッダーの中でNameがSubject(件名)でValueが「テストメール」のメッセージのメッセージIDを取得
        if (header.getName().equals("Subject") && header.getValue().equals("テストメール")) {
            targetMessage = fullMessage;
        }
    }
}
```
先に言い訳をすると、このコードの書き方はあまり良くなく、この処理をまるごとメソッドに分けて、対象のメールが見つかったらリターンをするようなメソッドにするのがベターだし、私も普段だったら絶対にそうします。今回はネット上に公開するサンプルコードとして、すべての処理を同じメソッド内に書きたかったのでこうしました。

言い訳は置いておいて、やっていることとしては、取得したMessageのリストからメッセージIDをつかって、Gmail APIのmessagesのgetというAPIをつかって、件名や本文などのより詳細なデータを取得しています。その取得してきたデータで、対象となるメールを見つけるために、件名が「テストメール」のメールを探しています。

変数の説明

- __Message targetMessage__ : 対象のメールを格納するための変数

処理の説明

```java
// メールの全データを取得
Message fullMessage = service.users().messages().get(USER, message.getId()).execute();
```
とりあえずmessagesのgetの公式の説明は以下
[Users.messages : get; https://developers.google.com/gmail/api/v1/reference/users/messages/get](https://developers.google.com/gmail/api/v1/reference/users/messages/get)

引数にユーザとメッセージIDを入れて投げると、より詳細なメールの情報が返ってくる。

```json
{
    "historyId": "212543",
    "id": "1635e36ac0cf27ce",
    "internalDate": "1526294031000",
    "labelIds": [
        "IMPORTANT",
        "Label_8",
        "SENT"
    ],
    "payload": {
        "body": {
            "size": 0
        },
        "filename": "",
        "headers": [
            {
                "name": "MIME-Version",
                "value": "1.0"
            },
            {
                "name": "Received",
                "value": "by 10.179.15.206 with HTTP; Mon, 14 May 2018 03:33:51 -0700 (PDT)"
            },
            {
                "name": "Date",
                "value": "Mon, 14 May 2018 19:33:51 +0900"
            },
            {
                "name": "Delivered-To",
                "value": "xxx@gmail.com"
            },
            {
                "name": "Message-ID",
                "value": "<CAJsBAS7wAJm+gaUETbUzR1fyshn=zgu4mb5sexdKAv7+jHbmCA@mail.gmail.com>"
            },
            {
                "name": "Subject",
                "value": "テストメール"
            },
            {
                "name": "From",
                "value": "Fuga Hoge <xxx@gmail.com>"
            },
            {
                "name": "To",
                "value": "xxx@gmail.com"
            },
            {
                "name": "Content-Type",
                "value": "multipart/mixed; boundary=\"0000000000009360f1056c2805b7\""
            }
        ],
        "mimeType": "multipart/mixed",
        "partId": "",
        "parts": [
            {
                "body": {
                    "size": 0
                },
                "filename": "",
                "headers": [
                    {
                        "name": "Content-Type",
                        "value": "multipart/alternative; boundary=\"0000000000009360ec056c2805b5\""
                    }
                ],
                "mimeType": "multipart/alternative",
                "partId": "0",
                "parts": [
                    {
                        "body": {
                            "data": "5pys5paH44G744Gr44KD44KJ44KJDQo=",
                            "size": 23
                        },
                        "filename": "",
                        "headers": [
                            {
                                "name": "Content-Type",
                                "value": "text/plain; charset=\"UTF-8\""
                            },
                            {
                                "name": "Content-Transfer-Encoding",
                                "value": "base64"
                            }
                        ],
                        "mimeType": "text/plain",
                        "partId": "0.0"
                    },
                    {
                        "body": {
                            "data": "PGRpdiBkaXI9Imx0ciI-5pys5paH44G744Gr44KD44KJ44KJPGJyPjwvZGl2Pg0K",
                            "size": 48
                        },
                        "filename": "",
                        "headers": [
                            {
                                "name": "Content-Type",
                                "value": "text/html; charset=\"UTF-8\""
                            },
                            {
                                "name": "Content-Transfer-Encoding",
                                "value": "base64"
                            }
                        ],
                        "mimeType": "text/html",
                        "partId": "0.1"
                    }
                ]
            },
            {
                "body": {
                    "attachmentId": "ANGjdJ_kQ0lQ3VjL86PDW2-qlN7bEQf-PLcRtzgFKvkg49Mi7KGwxlS8G2sn0LcOqdjM3D9CkVyVhmu1xbdDrxJC-GaaU7cBTUr4KUbwtENCONY9p0WW0stx8QuCEF5fnC2ZkKBFjOTxLqH3QJn88RGz-1IaE8l2wdexLgCP7MhYRNVx5Q0UCuvykLJO2WU3_uyJZWyZoVz5SxJz7_kXtLLv7X58p24HE3UU3Bxi3WIp5wpznsg1Z5GOmkYNTe29f7ag52B7L0p5SS_hIeGd73WqkgkH_Hma7Qmn0fPgd6wu_HUT8sqITt96CTfrl0M",
                    "size": 124626
                },
                "filename": "linux.png",
                "headers": [
                    {
                        "name": "Content-Type",
                        "value": "image/png; name=\"linux.png\""
                    },
                    {
                        "name": "Content-Disposition",
                        "value": "attachment; filename=\"linux.png\""
                    },
                    {
                        "name": "Content-Transfer-Encoding",
                        "value": "base64"
                    },
                    {
                        "name": "X-Attachment-Id",
                        "value": "f_jh6421y31"
                    }
                ],
                "mimeType": "image/png",
                "partId": "1"
            }
        ]
    },
    "sizeEstimate": 171687,
    "snippet": "本文ほにゃらら",
    "threadId": "1635e36ac0cf27ce"
}
```
こんな感じでかなり長い。

```java
// ヘッダーを見る
for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
    // ヘッダーの中でNameがSubject(件名)でValueが「テストメール」のメッセージのメッセージIDを取得
    if (header.getName().equals("Subject") && header.getValue().equals("テストメール")) {
        targetMessage = fullMessage;
    }
}
```
取得したJSONのpayloadのheader内に、キーが"Subject"のデータがあり、そいつが件名である。その件名が「テストメール」であるやつを探している。

##本文のデコード
取得したJSONの中にキーが"snippet"のもので、値がそれっぽい"本文ほにゃらら"となっているものがあるが、こいつは本文ではなくスニペットで、メールの一覧を表示したときに、内容を知らせるために本文を少し表示するやつのデータなので違う。今回のテスト用のメールは本文が短いため、全文が入っているが、基本的には全文が入らないものと思ったほうが良い。

![20.png](https://qiita-image-store.s3.amazonaws.com/0/257548/53993827-0697-0b04-e3b6-329a9b17afc2.png)
_こいつがスニペット_

本当の本文はpayloadのParts内のさらに深いところにあって、しかもBase64エンコーディングされているので、読めるようにUTF8等にデコードしなくてはならない。

```java
// 本文の取得
// 本文の取得(Base64)
String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();
// Base64からデコード(byte列)
byte[] bodyBytes = Base64.decodeBase64(bodyBase64);
// byte列からStringに変換
String body = new String(bodyBytes, "UTF-8");
```

処理の説明

```java
// 本文の取得(Base64)
String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();
```
ご覧の通りに訳の分からない深い位置に本文がある。なんでこうなったかは上の生JSONを見て欲しい。getメソッドは戻りのクラスがObject型なので、なんとなくtoString()をしたら、期待していた形式のデータが取れた。ちなみにBASE64エンコーディングされている。

```java
// Base64からデコード(byte列)
byte[] bodyBytes = Base64.decodeBase64(bodyBase64);
```
とりあえずBASE64からUTF8のbyte列へ変換。はやくStringになってほしい。

```java
// byte列からStringに変換
String body = new String(bodyBytes, "UTF-8");
```
これでやっとStringになりました。こいつが本文です。

##添付ファイルのダウンロード
最後に添付ファイルのダウンロードです。

```java
// 添付ファイルの取得
// PayloadのPartを探索
for (MessagePart part : targetMessage.getPayload().getParts()) {
    String fileName = part.getFilename();
    // ファイル名がnullまたは""でない場合は添付ファイルなのでダウンロードを行う
    if (fileName != null && fileName.length() > 0) {
        // ファイルをダウンロード(Base64)
        MessagePartBody attachPart = service.users().messages().attachments()
                .get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
        // Base64からデコード
        byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
        // システムに保存
        FileOutputStream fileOutFile = new FileOutputStream(fileName);
        fileOutFile.write(fileByteArray);
        fileOutFile.close();
    }
}
```

添付ファイルは、Gmail APIのmessage.attachments.getを使います。とりあえず公式サイトの説明は以下。アタッチメントIDとメッセージIDで、BASE64エンコーディングされた添付ファイルが取得できます。

[Users.messages.attachments: get ; https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get](https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get)

処理の説明

```java
// 添付ファイルの取得
// PayloadのPartを探索
for (MessagePart part : targetMessage.getPayload().getParts()) {
    String fileName = part.getFilename();
    // ファイル名がnullまたは""でない場合は添付ファイルなのでダウンロードを行う
    if (fileName != null && fileName.length() > 0) {
        //省略
    }
}
```
payloadのparts内にファイル名とアタッチメントIDが格納されています。しかしながら、同じ階層に本文などもあり、その場合はfileNameが""などになっているはずなので、そいつらを避けてアタッチメントIDを取得します。

```java
// 添付ファイルの取得
// PayloadのPartを探索
if (fileName != null && fileName.length() > 0) {
    // ファイルをダウンロード(Base64)
    MessagePartBody attachPart = service.users().messages().attachments().get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
    // Base64からデコード
    byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
    // システムに保存
    FileOutputStream fileOutFile = new FileOutputStream(fileName);
    fileOutFile.write(fileByteArray);
    fileOutFile.close();
}
```
アタッチメントIDが取得できたので、それを利用してGmail APIのmessage.attachments.getを使って添付ファイルを取得します。取得した添付ファイルはBASE64エンコーディングされているので、byte列へデコードします。そして最後にデコードしたbyte列をファイルへ保存します。実行すると以下のような感じに、プロジェクト直下に添付ファイルがダウンロードされているはず。
![21.png](https://qiita-image-store.s3.amazonaws.com/0/257548/82151b15-96fb-b491-bdff-306300848c1e.png)

#さいごに
ここで書いたコードを全部つなげると以下のような感じ。

```java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.repackaged.org.apache.commons.codec.binary.Base64;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.ListMessagesResponse;
import com.google.api.services.gmail.model.Message;
import com.google.api.services.gmail.model.MessagePart;
import com.google.api.services.gmail.model.MessagePartBody;
import com.google.api.services.gmail.model.MessagePartHeader;

public class Test {
    public static void main(String[] args) throws IOException, GeneralSecurityException {
        // Json処理
        JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
        // スコープ
        List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_READONLY);
        // 秘密鍵
        String CLIENT_SECRET_DIR = "client_secret.json";
        // 認証周りの設定ファイルの格納フォルダ
        String CREDENTIALS_FOLDER = "credentials";
        // HTTP通信
        NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

        // 認証
        // 秘密鍵の読み込み
        FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
        InputStreamReader reader = new InputStreamReader(in);
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);
        // 認証の設定
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
                clientSecrets, SCOPES)
                        .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
                        .setAccessType("offline").build();
        // 認証を行う
        Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");

        // Gmailのサービスを作成
        // アプリケーション名
        String APPLICATION_NAME = "Test1";
        // Gmailにアクセス
        Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME)
                .build();

        // メールの取得
        // ユーザ
        String USER = "me";
        // メールを全て取得(メールIDしか入ってない)
        ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
        List<Message> messageList = messagesResponse.getMessages();

        // 対象のメールを見つけるためにメールの全データを探索する
        Message targetMessage = null;
        for (Message message : messageList) {
            // メールの全データを取得
            Message fullMessage = service.users().messages().get(USER, message.getId()).execute();
            // ヘッダーを見る
            for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
                // ヘッダーの中でNameがSubject(件名)でValueが「テストメール」のメッセージのメールIDを取得
                if (header.getName().equals("Subject") && header.getValue().equals("テストメール")) {
                    targetMessage = fullMessage;
                }
            }
        }

        // 本文の取得
        // 本文の取得(Base64)
        String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();
        // Base64からデコード(byte列)
        byte[] bodyBytes = Base64.decodeBase64(bodyBase64);
        // byte列からStringに変換
        String body = new String(bodyBytes, "UTF-8");
        // 本文を標準出力
        System.out.println(body);

        // 添付ファイルの取得
        // PayloadのPartを探索
        for (MessagePart part : targetMessage.getPayload().getParts()) {
            String fileName = part.getFilename();
            // ファイル名がnullまたは""でない場合は添付ファイルなのでダウンロードを行う
            if (fileName != null && fileName.length() > 0) {
                // ファイルをダウンロード(Base64)
                MessagePartBody attachPart = service.users().messages().attachments()
                        .get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
                // Base64からデコード
                byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
                // システムに保存
                FileOutputStream fileOutFile = new FileOutputStream(fileName);
                fileOutFile.write(fileByteArray);
                fileOutFile.close();
            }
        }
    }
}

```


これでめでたくGmailのメールを自動で取得できるようになり、それに付随した業務とかを自動化できるようになってハッピーになれるかもしれません。ここで丁寧に説明したGmail APIも以下のURLで、公式がアメリカ語ではありますが使い方を説明しているので、それを読めば割と何でもできると思います。

[API Reference ; https://developers.google.com/gmail/api/v1/reference/](https://developers.google.com/gmail/api/v1/reference/)

個人的なGmail APIを使った感想としては、件名や本文などを取得するときにpayloadやらpartsやらを探索するのが面倒だったので、Messageクラスさんが頑張ってそこらへんのフィールドを持っていて、自動でそいつらに入ってくれると嬉しいなぁという感じです。