はじめに

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

やりたいこと

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

うれしいこと

よくある会社のネットワークの構成で、外部への通信は全てプロキシサーバを通していて、基本的にHttpとHttps以外は外に出れないような設定になっているときとかに、IMAPとかは外に出れなくてかなしいけど、Gmail APIはHttpsなので外に出れるからうれしい。

javaでのhttpとhttpsのプロキシ設定は以下らへんを使えばいけるはず。
-Dhttp.proxyHost=hostname
-Dhttp.proxyPort=port
-Dhttp.proxyUser=user
-Dhttp.proxyPassword=password
-Dhttps.proxyHost=hostname
-Dhttps.proxyPort=port
-Dhttps.proxyUser=user
-Dhttps.proxyPassword=password

前提

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

取得したいメール

今回で取得するためのサンプルのメールです。
19.png
添付ファイルでLinuxのペンギンの画像をつけておきました。

ライブラリの取り込み

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

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

1. zipをダウンロードする
1.png

2. zipを解凍すると直下、libs、libs-sourcesフォルダ内にjarファイルがあるので、そいつらが取り込む対象
2.png

3. プロジェクトにjarファイルを入れるために、プロジェクト内に適当にフォルダをつくる
(プロジェクトを右クリック->新規->フォルダ)
3.png

4. 作ったフォルダにjarファイルを全部入れる
4.png

5. javaさんがjarファイルを読み込んでくれるためにビルドパスに追加する
(jarファイルを選択して右クリック->ビルド・パス->ビルドパスに追加)
5.png
※libs、libs-sourcesフォルダ内も忘れないように

6. これで終わりかと思いきや、実行時にクラスが見つからんと怒られることがあり、なにかと思ったら、ライブラリ内で呼んでいるJavaServletが無いらしいので、以下から適当なバージョンのjarを取得して、同様にビルドパスに追加する。
Maven Repository: javax.servlet » servlet-api ; https://mvnrepository.com/artifact/javax.servlet/servlet-api

認証の設定

以下の2つをやる。

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

プロジェクトを作成

以下のURLにアクセスしてプロジェクトをつくる。
リソースの管理 ; https://console.developers.google.com/cloud-resource-manager
1. 「プロジェクトを作成」をクリック
13.png

2. 適当にプロジェクト名をつけて「作成」をクリック
14.png

3. しばらくするとプロジェクトができる
少し待ってよう
15.png

Gmail APIを有効化

以下のURLからGoogleのAPIライブラリにアクセスしてGmail APIを有効化する。
API ライブラリ ; https://console.developers.google.com/apis/library
6.png

OAuth 2.0 クライアントを用意

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

1. 認証情報を作成をクリック
7.png

2. OAuthクライアントIDを選択
8.png

3. 「その他」を選択して、適当に名前をつけて、「作成」をクリック
9.png

4. 完成
11.png

5. あとで使うので秘密鍵をダウンロードしておく
なんでもいいけど「client_secret.json」って名前で保存しておきましょう。
12.png

6. OAuth同意画面も適当に作成
10.png

Javaを書いてみる

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

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

認証

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

1. プロジェクト内に前で取得した秘密鍵の「client_secret.json」を配置する
16.png

2. 秘密鍵を元にリフレッシュトークンとかの認証に必要な情報を取得するのだが、それを保存するためのフォルダをプロジェクト内に作る
とりあえず名前は「credentials」にしておく。
17.png

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

// 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通信をするためのオブジェクト。

続いて処理の説明

// 秘密鍵の読み込み
FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
InputStreamReader reader = new InputStreamReader(in);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);

直下にある秘密鍵の「client_secret.json」を読み込んでます

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"で
// 認証を行う
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");

実際にHTTP通信をして、秘密鍵を投げて、トークンとかアクセストークンとかを貰って、指定したフォルダに保存します。
実行すると以下のように、フォルダ内になんかファイルができているはずです。
18.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

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

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

// 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のサービスのオブジェクトにつける名前。適当でいい。

処理の説明

// 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のサービスの名前
// メールの取得
// ユーザ
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ができます。

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

メールの取得

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

// 対象のメールを見つけるためにメールの全データを探索する
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 : 対象のメールを格納するための変数

処理の説明

// メールの全データを取得
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

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

{
    "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"
}

こんな感じでかなり長い。

// ヘッダーを見る
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
こいつがスニペット

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

// 本文の取得
// 本文の取得(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");

処理の説明

// 本文の取得(Base64)
String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();

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

// Base64からデコード(byte列)
byte[] bodyBytes = Base64.decodeBase64(bodyBase64);

とりあえずBASE64からUTF8のbyte列へ変換。はやくStringになってほしい。

// byte列からStringに変換
String body = new String(bodyBytes, "UTF-8");

これでやっとStringになりました。こいつが本文です。

添付ファイルのダウンロード

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

// 添付ファイルの取得
// 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

処理の説明

// 添付ファイルの取得
// PayloadのPartを探索
for (MessagePart part : targetMessage.getPayload().getParts()) {
    String fileName = part.getFilename();
    // ファイル名がnullまたは""でない場合は添付ファイルなのでダウンロードを行う
    if (fileName != null && fileName.length() > 0) {
        //省略
    }
}

payloadのparts内にファイル名とアタッチメントIDが格納されています。しかしながら、同じ階層に本文などもあり、その場合はfileNameが""などになっているはずなので、そいつらを避けてアタッチメントIDを取得します。

// 添付ファイルの取得
// 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

さいごに

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

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/

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

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.