I tried using Gmail's API in Java, but even if I googled it, I was sad that only Stack Overflow and English articles on Google's official website came out, so I had to write an article in Japanese. thought.
I want to get the body and attachments of an email received on Gmail using Java.
--I use Eclipse. --Prepare a Google account.
This is a sample email to get this time. I have attached an image of a Linux penguins in the attached file.
First, in preparation for using the Gmail API, get the Gmail API library from the following URL. Gmail API Client Library for Java ; https://developers.google.com/api-client-library/java/apis/gmail/v1
If you are using MAVEN and GRADLE, you can install the library by following the instructions at the link. If you can't use such a convenience for various reasons, download it as a zip and include all the jar files inside.
Do the following two things.
--Create a project on your Google account. --Enable the Gmail API on your Google account. --Since the Gmail API uses OAuth 2.0 for authentication, prepare an OAuth 2.0 client on your Google account.
Access the following URL and create a project. Resource Management; https://console.developers.google.com/cloud-resource-manager __1. Click "Create Project" __
__2. Give a suitable project name and click "Create" __
__3. A project will be created after a while __ Let's wait a moment
Access Google's API library from the following URL and enable the Gmail API. API Library; https://console.developers.google.com/apis/library
Access the following URL and create an OAuth 2.0 client. Credentials; https://console.developers.google.com/apis/credentials
__1. Click Create Credentials __
__2. Select OAuth Client ID __
__3. Select "Other", give it an appropriate name, and click "Create" __
__4. Completion __
__5. Download the private key for later use __ Anything is fine, but let's save it as "client_secret.json".
__6. Create OAuth consent screen appropriately __
The processing flow is roughly as follows, so we will proceed in order.
Based on the private key obtained earlier, perform authentication work to obtain a token for using the API and a refresh token for reissuing the token.
__1. Place the previously obtained private key "client_secret.json" in the project __
__2. I get the information necessary for authentication such as refresh token based on the private key, but create a folder in the project to save it __ For the time being, name it "credentials".
__3. Finally coding __ Get a token to use the API and a refresh token to reissue the token based on the private key
//Json processing
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
//scope
List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_READONLY);
//Private key
String CLIENT_SECRET_DIR = "client_secret.json";
//Storage folder for configuration files around authentication
String CREDENTIALS_FOLDER = "credentials";
//HTTP communication
NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
//Authentication
//Reading the private key
FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
InputStreamReader reader = new InputStreamReader(in);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);
//Authentication settings
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
.setAccessType("offline").build();
//Authenticate
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
First of all, explanation of constants
--JSON_FACTORY_: Object for processing JSON. The data returned by the API is in JSON format. --SCOPES: How much access should you allow to Gmail using the API? There should be all the constants in the GmailScopes class, so choose the appropriate one and use it. If you select GMAIL_READONLY, you should be able to do everything you want to do this time, so let's select it. --CLIENT_SECRET_DIR: The path of the private key file. This time, I put the name "client_secret.json" directly under it, so "client_secret.json" is OK. --CREDENTIALS_FOLDER: The path of the folder to save the files around the authentication obtained by using the private key. Set the folder created above. --HTTP_TRANSPORT: Object for HTTP communication.
Next, explanation of processing
//Reading the private key
FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
InputStreamReader reader = new InputStreamReader(in);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);
I'm reading the private key "client_secret.json" directly underneath
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
.setAccessType("offline").build();
I put the setting to throw the authentication.
--new GoogleAuthorizationCodeFlow.Builder argument --__ HTTP communication object __: HTTP_TRANSPORT --_JSON processing object : JSON_FACTORY -- private key : clientSecrets -- Scope : SCOPES (the one set to READ ONLY) --SetDataStoreFactory argument -- Output destination of files around authentication obtained using private key __ --setAccessType argument --_Offline access __ (not human access on the browser), so use "offline"
//Authenticate
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
Actually perform HTTP communication, throw the private key, get the token or access token, and save it in the specified folder. When you run it, you should see some files in the folder as shown below.
I use an API called list of messages. messages is an API for getting, sending, and manipulating emails, and list literally gets a list of received emails. Since it is just a list, you can only get the message ID and thread ID, and you cannot see the text. Use get in messages to get the text and detailed data. For the time being, the explanation of the official website is as follows (American)
Users.messages: list ; https://developers.google.com/gmail/api/v1/reference/users/messages/list
It is convenient to read because it contains the information required to throw the API, the required scope, the JSON definition to be returned, and sample code in various languages. It's American.
The first thing to do is to create an object for the Gmail service, execute the messages list on that object, and put the returned data into the List.
//Create a Gmail service
//Application name
String APPLICATION_NAME = "Test1";
//Access Gmail
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();
//Get email
//User
String USER = "me";
//Get all emails(Only the message ID is included)
ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
List<Message> messageList = messagesResponse.getMessages();
Description of constants
--APPLICATION_NAME: The name to give to the Gmail service object you create. Appropriate.
Description of processing
//Create a Gmail service
//Application name
String APPLICATION_NAME = "Test1";
//Access Gmail
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();
I'm creating an object for a Gmail service.
--New Gmail.Builder argument --__ HTTP communication object __: HTTP_TRANSPORT --_JSON processing object : JSON_FACTORY -- Credentials __: credential
//Get email
//User
String USER = "me";
//Get all emails(Only the message ID is included)
ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
List<Message> messageList = messagesResponse.getMessages();
I run a list of messages and store the results in a List. The argument is user only. I want to see my email, so just put "me" in it. This will create a JSON List that contains only the message ID and thread ID, as shown below.
{
"id": "1635e36ac0cf27ce",
"threadId": "1635e36ac0cf27ce"
}
Further obtain detailed data such as subject and body from the message ID to narrow down the target emails.
//Explore all email data to find the email in question
Message targetMessage = null;
for (Message message : messageList) {
//Get all email data
Message fullMessage = service.users().messages().get(USER, message.getId()).execute();
//View header
for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
//Name is Subject in the header(subject)Get the message ID of the message whose Value is "test mail"
if (header.getName().equals("Subject") && header.getValue().equals("Test email")) {
targetMessage = fullMessage;
}
}
}
To make an excuse first, this code is not very well written, and it is better to divide this process into methods and return when the target email is found, and I would definitely do it. I will do so. This time, as a sample code to be published on the net, I wanted to write all the processing in the same method, so I did this.
Aside from excuses, what I'm doing is using the message ID from the list of retrieved Messages and using the Gmail API's get API to get more detailed data such as the subject and body. I am. We are looking for an email with the subject "test email" to find the target email in the acquired data.
Variable description
--Message targetMessage: Variable for storing the target email
Description of processing
//Get all email data
Message fullMessage = service.users().messages().get(USER, message.getId()).execute();
For the time being, the official explanation of get for messages is as follows Users.messages : get; https://developers.google.com/gmail/api/v1/reference/users/messages/get
If you put the user and message ID in the argument and throw it, more detailed mail information will be returned.
{
"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": "[email protected]"
},
{
"name": "Message-ID",
"value": "<CAJsBAS7wAJm+gaUETbUzR1fyshn=zgu4mb5sexdKAv7+jHbmCA@mail.gmail.com>"
},
{
"name": "Subject",
"value": "Test email"
},
{
"name": "From",
"value": "Fuga Hoge <[email protected]>"
},
{
"name": "To",
"value": "[email protected]"
},
{
"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": "Body Honyara",
"threadId": "1635e36ac0cf27ce"
}
It's quite long like this.
//View header
for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
//Name is Subject in the header(subject)Get the message ID of the message whose Value is "test mail"
if (header.getName().equals("Subject") && header.getValue().equals("Test email")) {
targetMessage = fullMessage;
}
}
There is data with the key "Subject" in the header of the acquired JSON payload, and that is the subject. I'm looking for someone whose subject is "test email".
Some of the acquired JSON has a key of "snippet" and a value like that, "Body Honyara", but this is a snippet instead of the body, and when the list of emails is displayed Besides, it is different because it is the data of the one who displays the text a little to inform the contents. Since the body of this test email is short, it contains the full text, but basically it is better to think that the full text is not included.
_ This is a snippet _
The real text is deeper in the payload Parts and is Base64 encoded, so it must be decoded into UTF8 etc. for readability.
//Acquisition of text
//Acquisition of text(Base64)
String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();
//Decode from Base64(byte string)
byte[] bodyBytes = Base64.decodeBase64(bodyBase64);
//Convert from byte string to String
String body = new String(bodyBytes, "UTF-8");
Description of processing
//Acquisition of text(Base64)
String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();
As you can see, the text is in an incomprehensible deep position. See the raw JSON above to see why this happened. Since the return class of the get method is Object type, I managed to get the data in the expected format by doing toString (). By the way, it is BASE64 encoded.
//Decode from Base64(byte string)
byte[] bodyBytes = Base64.decodeBase64(bodyBase64);
Convert from BASE64 to UTF8 byte string for the time being. I want you to become a String soon.
//Convert from byte string to String
String body = new String(bodyBytes, "UTF-8");
Now it's finally a String. This is the text.
Finally, download the attached file.
//Get attachments
//Explore Payload Part
for (MessagePart part : targetMessage.getPayload().getParts()) {
String fileName = part.getFilename();
//File name is null or""If not, it is an attached file, so download it.
if (fileName != null && fileName.length() > 0) {
//Download file(Base64)
MessagePartBody attachPart = service.users().messages().attachments()
.get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
//Decode from Base64
byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
//Save to system
FileOutputStream fileOutFile = new FileOutputStream(fileName);
fileOutFile.write(fileByteArray);
fileOutFile.close();
}
}
For attachments, use the Gmail API message.attachments.get. For the time being, the explanation of the official website is as follows. You can get a BASE64-encoded attachment with the attachment ID and message ID.
Description of processing
//Get attachments
//Explore Payload Part
for (MessagePart part : targetMessage.getPayload().getParts()) {
String fileName = part.getFilename();
//File name is null or""If not, it is an attached file, so download it.
if (fileName != null && fileName.length() > 0) {
//abridgement
}
}
The file name and attachment ID are stored in the parts of the payload. However, there is also a text etc. in the same hierarchy, in which case the fileName should be "" etc., so avoid them and get the attachment ID.
//Get attachments
//Explore Payload Part
if (fileName != null && fileName.length() > 0) {
//Download file(Base64)
MessagePartBody attachPart = service.users().messages().attachments().get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
//Decode from Base64
byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
//Save to system
FileOutputStream fileOutFile = new FileOutputStream(fileName);
fileOutFile.write(fileByteArray);
fileOutFile.close();
}
Now that you have the attachment ID, use it to get the attachments using the Gmail API message.attachments.get. Since the acquired attachment file is BASE64 encoded, it is decoded into a byte string. Then save the last decoded byte string to a file. When you execute it, the attached file should be downloaded directly under the project as shown below.
If you connect all the code written here, it looks like the following.
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 processing
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
//scope
List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_READONLY);
//Private key
String CLIENT_SECRET_DIR = "client_secret.json";
//Storage folder for configuration files around authentication
String CREDENTIALS_FOLDER = "credentials";
//HTTP communication
NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
//Authentication
//Reading the private key
FileInputStream in = new FileInputStream(new File(CLIENT_SECRET_DIR));
InputStreamReader reader = new InputStreamReader(in);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, reader);
//Authentication settings
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
.setAccessType("offline").build();
//Authenticate
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
//Create a Gmail service
//Application name
String APPLICATION_NAME = "Test1";
//Access Gmail
Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME)
.build();
//Get email
//User
String USER = "me";
//Get all emails(Only email ID is included)
ListMessagesResponse messagesResponse = service.users().messages().list(USER).execute();
List<Message> messageList = messagesResponse.getMessages();
//Explore all email data to find the email in question
Message targetMessage = null;
for (Message message : messageList) {
//Get all email data
Message fullMessage = service.users().messages().get(USER, message.getId()).execute();
//View header
for (MessagePartHeader header : fullMessage.getPayload().getHeaders()) {
//Name is Subject in the header(subject)Get the email ID of the message whose Value is "test email"
if (header.getName().equals("Subject") && header.getValue().equals("Test email")) {
targetMessage = fullMessage;
}
}
}
//Acquisition of text
//Acquisition of text(Base64)
String bodyBase64 = targetMessage.getPayload().getParts().get(0).getParts().get(0).getBody().get("data").toString();
//Decode from Base64(byte string)
byte[] bodyBytes = Base64.decodeBase64(bodyBase64);
//Convert from byte string to String
String body = new String(bodyBytes, "UTF-8");
//Standard output of text
System.out.println(body);
//Get attachments
//Explore Payload Part
for (MessagePart part : targetMessage.getPayload().getParts()) {
String fileName = part.getFilename();
//File name is null or""If not, it is an attached file, so download it.
if (fileName != null && fileName.length() > 0) {
//Download file(Base64)
MessagePartBody attachPart = service.users().messages().attachments()
.get(USER, targetMessage.getId(), part.getBody().getAttachmentId()).execute();
//Decode from Base64
byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
//Save to system
FileOutputStream fileOutFile = new FileOutputStream(fileName);
fileOutFile.write(fileByteArray);
fileOutFile.close();
}
}
}
}
With this, you will be able to automatically retrieve Gmail emails, and you may be happy to be able to automate the tasks that accompany it. The Gmail API explained carefully here is also at the following URL, and although the official language is American, it explains how to use it, so if you read it, you can do anything.
API Reference ; https://developers.google.com/gmail/api/v1/reference/
As a personal impression using the Gmail API, it was troublesome to search the payload and parts when getting the subject and body, so the Message class did its best and had a lot of fields. I'm glad if you can join them automatically.
Recommended Posts