Use Java Gmail API to send an email with multiple (large) attachments - java

I am trying to use the Google Gmail API (Java) to create an email that contains multiple attachments. Using the code below, I am able to send multiple attachments that are embedded within a MimeMessage if the attachments total less than 5MB (Google's threshold for simple file upload).
com.google.api.services.gmailGmail service = (... defined above ...)
javax.mail.internet.MimeMessage message = (... defined above with attachments embedded ...)
// Send the email
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
mimeMessage.writeTo(buffer);
byte[] bytes = buffer.toByteArray();
String encodedEmail = Base64.encodeBase64URLSafeString(bytes);
Message message = new Message();
message.setRaw(encodedEmail);
message = service.users().messages().send("me", message).execute();
However, I am unable to figure out how to correctly attach multiple files to an email using the Gmail Java API. The method below looks promising, but it appears to only accept 1 File/InputStream (mediaContent).
Gmail.Users.Messages.Send send(userId, Message content, AbstractInputStreamContent mediaContent)
Anyone know how to correctly implement a multi-file upload using the API?

As you correctly stated, the maximum attachment size for Simple file upload is 5 MB
Conclusion:
You need to to use Multipart upload or Resumable upload.
A sample sending an email with a multipart upload:
public static MimeMessage createEmailWithAttachment(String to, String from, String subject,
String bodyText,String filePath) throws MessagingException{
File file = new File(filePath);
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage email = new MimeMessage(session);
Multipart multipart = new MimeMultipart();
InternetAddress tAddress = new InternetAddress(to);
InternetAddress fAddress = new InternetAddress(from);
email.setFrom(fAddress);
email.addRecipient(javax.mail.Message.RecipientType.TO, tAddress);
email.setSubject(subject);
if (file.exists()) {
source = new FileDataSource(filePath);
messageFilePart = new MimeBodyPart();
messageBodyPart = new MimeBodyPart();
try {
messageBodyPart.setText(bodyText);
messageFilePart.setDataHandler(new DataHandler(source));
messageFilePart.setFileName(file.getName());
multipart.addBodyPart(messageBodyPart);
multipart.addBodyPart(messageFilePart);
email.setContent(multipart);
} catch (MessagingException e) {
e.printStackTrace();
}
}else
email.setText(bodyText);
return email;
}
Here you can find many other useful samples for sending emails with the Gmail API in Java.

It turns out that my MimeMessage was generated correctly, however, if the attachments included in the MimeMessage are larger than 5MB, you need to use a different Gmail API send() method. The API docs are incredibly confusing because they appear to state that you need to make multiple calls to rest endpoints to upload multiple files. It turns out that the Gmail Java Api does all the for you based off the MimeMessage submitted.
Below is a code snippet that shows how to use the two methods: "simple upload" and "multipart upload".
com.google.api.services.gmailGmail service = (... defined above ...)
javax.mail.internet.MimeMessage message = (... defined above with attachments embedded ...)
/**
* Send email using Gmail API - dynamically uses simple or multipart send depending on attachments size
*
* #param mimeMessage MimeMessage (includes any attachments for the email)
* #param attachments the Set of files that were included in the MimeMessage (if any). Only used to calculate total size to see if we should use "simple" send or need to use multipart upload.
*/
void send(MimeMessage mimeMessage, #Nullable Set<File> attachments) throws Exception {
Message message = new Message();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
mimeMessage.writeTo(buffer);
// See if we need to use multipart upload
if (attachments!=null && computeTotalSizeOfAttachments(attachments) > BYTES_5MB) {
ByteArrayContent content = new ByteArrayContent("message/rfc822", buffer.toByteArray());
message = service.users().messages().send("me", null, content).execute();
// Otherwise, use "simple" send
} else {
String encodedEmail = Base64.encodeBase64URLSafeString(buffer.toByteArray());
message.setRaw(encodedEmail);
message = service.users().messages().send("me", message).execute();
}
System.out.println("Gmail Message: " + message.toPrettyString());
}

Related

Is it possible to add inline bitmap object to the body of an email sent via JavaMail?

I'd like to add an inline bitmap (generated within the code) to an email to be sent via JavaMail in Android. Below is my code currently:
try {
// Compose the message
// javax.mail.internet.MimeMessage class is
// mostly used for abstraction.
Message message = new MimeMessage(session);
// header field of the header.
message.setFrom(new InternetAddress("service#someone.com"));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient));
message.setSubject("Workside Verification Service");
message.setText(
"Thank you for registering. Please click on the following link to activate your account:\n\n"
+ urlWithToken
+ "\n\nRegards,\nThe Workside Team");
// Add the generated QR code bitmap here
Multipart multipart = new MimeMultipart("related");
MimeBodyPart imgPart = new MimeBodyPart();
// imageFile is the file containing the image
// TODO - pass bitmap to imageFile below
File file = new File(null);
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
mBitmapQR.compress(Bitmap.CompressFormat.PNG, 90, os);
imgPart.attachFile(imageFile);
multipart.addBodyPart(imgPart);
message.setContent(multipart);
Transport.send(message); // send Message
System.out.println("Email Sent");
} catch (MessagingException | FileNotFoundException e) {
throw new RuntimeException(e);
}
I was thinking of converting the bitmap to a File object and then adding it to the body of the message, but I was thinking that there could be a more straightfirward and efficient way.
The Jakarta Mail FAQ is your best resource. See How do I send HTML mail that includes images?. That describes 3 choices:
Link image to web site, which I doubt works for you.
Inline the image <img src="data:image/jpeg;base64,base64-encoded-data-here" />
Construct a multipart/related message as you are doing.
The issue, as seen from the code, was that I was adding the text to the multipart, then the image (effectively overriding the text), and then I was assigning the multipart to the message. The solution was to add the text, using addBodyPart(text), and then use addBodyPart(image). After that, I could use setContent(multipart) to properly assign the text and image to the email.
// Add the generated QR code bitmap here
Multipart multipart = new MimeMultipart("related");
MimeBodyPart imgPart = new MimeBodyPart();
// Set the cache path and generate the new file image
String mFilePath = mContext.getCacheDir().toString();
File file = new File(mFilePath, FILE_NAME);
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
// TITLE
message.setSubject("Workside Verification Service");
// TEXT
MimeBodyPart txtPart = new MimeBodyPart();
txtPart.setContent("Welcome to Workside! \n\nPlease proceed by scanning the QR code provided using the Workside application available in the Google Play store.\n\n\n"
+ "Regards,\n\nThe Workside Team", "text/plain");
// ADD TEXT
multipart.addBodyPart(txtPart);
// Generate image using the QR Bitmap, and attach it
mBitmapQR.compress(Bitmap.CompressFormat.JPEG, 90, os);
imgPart.attachFile(mFilePath + FILE_NAME);
// ADD IMAGE
multipart.addBodyPart(imgPart);
message.setContent(multipart);

why the reply mail is sent as attachment when I send it to outlook account using javamail?

I tried in many ways to get the reply in same thread using outlook account and javamail api but iam not able to get reply in same thread instead iam getting as attachment.
I tried to copy whole content and save in current message even then iam getting as attachment, also tried to change the content disposition as inline still it didn't work
you can find the code below which i had tried.
Properties properties = new Properties();
Session emailSession = Session.getDefaultInstance(properties,null);
store = emailSession.getStore("imaps");
store.connect(host,mailbox_username, mailbox_password);
folder = store.getFolder("Inbox");
folder.open(Folder.READ_WRITE);
Message[] unreadMessages = folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN),false));
if(unreadMessages.size()>0)
{
for (int i = 0; i < unreadMessages.length; i++)
{
log.info("retriving message "+(i+1))
Message message = unreadMessages[i]
Address[] froms = message.getFrom();
String senderEmailAddress =(froms[0]).getAddress();
if(senderEmailAddress.endsWith("#gmail.com"))
{
subject = message.getSubject()
log.info(message.getSubject())
}
else
{ //reply to same mail here we need to reply to the message
Message message2 = new MimeMessage(emailSession);
message2= (MimeMessage) message.reply(false);
message2.setSubject("RE: " + message.getSubject());
//message2.setFrom(new InternetAddress(from));
message2.setReplyTo(message.getReplyTo());
message2.addRecipient(Message.RecipientType.TO, new InternetAddress(senderEmailAddress));
BodyPart messageBodyPart = new MimeBodyPart();
content = "some reply message"
//multipart.addBodyPart(content);
messageBodyPart.setText(content);
Multipart multipart = new MimeMultipart("related");
multipart.addBodyPart(messageBodyPart);
messageBodyPart = new MimeBodyPart();
//messageBodyPart.setDataHandler(message.getDataHandler());
//bodyPart.setDataHandler(new DataHandler(ds));
//messageBodyPart.setHeader("Content-Type", "image/jpeg; name=image.jpg");
//messageBodyPart.setHeader("Content-ID", "<image>");
//messageBodyPart.setHeader("Content-Disposition", "inline");
//messageBodyPart.addBodyPart(bodyPart);
//msg.setContent(content);
messageBodyPart.setDisposition(MimeBodyPart.INLINE);
messageBodyPart.setContent(message, "message/rfc822");
messageBodyPart.setDataHandler(message.getDataHandler());
// Add part to multi part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
message2.setContent(multipart);
Transport t = emailSession.getTransport("smtp");
try {
t.connect(mailbox_username, mailbox_password);
t.sendMessage(message2, message2.getAllRecipients());
} finally {
t.close();
}
}
}
}
"inline" vs. "attachment" is just advice for the mail reader. Many ignore the device, or aren't capable of displaying all content types inline.
If you want the text of the original message to appear in the body of the reply message (e.g., indented with ">"), you need to extract the original text and reformat it appropriately, adding it to the text of the reply, then set that new String as the content of the reply message.

What is maximum size for attachment for mimebodypart in sending email with Gmail API (Java)?

I am using Gmail Java SDK for sending email with attachment.
From this document, the maximum of uploading file size is 35 MB. (https://developers.google.com/gmail/api/v1/reference/users/messages/send).
However, In the reality, I only can send email with attachment with only 5MB maximum size, beyond that I get 400 Bad Request Too Large error from google.
This is my code for creating the mime message before send:
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage email = new MimeMessage(session);
email.setFrom(new InternetAddress(from));
email.setRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to));
email.setSubject(SUBJECT_RE + subject);
email.setReplyTo(new Address[]{new InternetAddress(from)});
String references = getMailReferences(messageId, service);
if(StringUtils.isNotEmpty(references)) {
email.setHeader(MAIL_HEADER_REFERENCES, references);
}
MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent(bodyText, "text/plain");
Multipart multipart = new MimeMultipart("mixed");
multipart.addBodyPart(mimeBodyPart);
for(int i=0 ; i< attachments.size() ; i++) {
EmailAttach attachment = attachments.get(i);
MimeBodyPart mimeBodyPartAttachment = new MimeBodyPart();
InputStream inputStream = new ByteArrayInputStream(attachment.getAttachmentBytes());
DataHandler dataHandler = new DataHandler(new InputStreamDataSource(inputStream, attachment.getFileName()));
mimeBodyPartAttachment.setDataHandler(dataHandler);
mimeBodyPartAttachment.setFileName(dataHandler.getName());
multipart.addBodyPart(mimeBodyPartAttachment);
}
email.setContent(multipart);
Is there any size limitation sending email with attachment with gmail or there is change needed in code to handle big attachment (example handle/creating MimeBodyPart) (>5MB)?
Solved, You need to use the method send with AbstractInputStreamContent parameter and DON'T do encode Base64 for the message content:
Message message = createMessageWithEmail(emailContent, threadId);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
emailContent.writeTo(buffer);
message = service.users().messages().send(userId, message, new ByteArrayContent("message/rfc822", buffer.toByteArray())).execute();

Need to open ms outlook with attachments [duplicate]

This question already has answers here:
Launch Outlook to compose a message with subject and attachment by Outlook's command line switches
(3 answers)
Closed 6 years ago.
I need to implement the below logic using java.
--> When i click on a button, MS Outlook need to get opened with To,CC,Subject and attachment.
We can use mailto for doing this but we can't add attachment if we use mailto.
i need to add multiple attachment from a shared folder to MS outlook
Please help me.
Using switched it is possible to have single attachment but i need to open outlook with 2+ attachment and send button should be available so that user can send the mail
Use JavaMail to create a multipart mime message with your To, CC, Subject and attachment. Then instead of transporting the message call saveChanges and writeTo and store the email to the file system.
There is an undocumented /eml switch that can be used to open the MIME standard format. For example, outlook /eml filename.eml There is a documented /f switch which will open msg files. For example outlook /f filename.msg The x-unsent can be used to toggle the send button.
Here is an example to get you started:
public static void main(String[] args) throws Exception {
//Create message envelope.
MimeMessage msg = new MimeMessage((Session) null);
msg.addFrom(InternetAddress.parse("you#foo.com"));
msg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse("support#bar.com"));
msg.setRecipients(Message.RecipientType.CC,
InternetAddress.parse("manager#baz.com"));
msg.setSubject("Hello Outlook");
//msg.setHeader("X-Unsent", "1");
MimeMultipart mmp = new MimeMultipart();
MimeBodyPart body = new MimeBodyPart();
body.setDisposition(MimePart.INLINE);
body.setContent("This is the body", "text/plain");
mmp.addBodyPart(body);
MimeBodyPart att = new MimeBodyPart();
att.attachFile("c:\\path to file.attachment");
mmp.addBodyPart(att);
msg.setContent(mmp);
msg.saveChanges();
File resultEmail = File.createTempFile("test", ".eml");
try (FileOutputStream fs = new FileOutputStream(resultEmail)) {
msg.writeTo(fs);
fs.flush();
fs.getFD().sync();
}
System.out.println(resultEmail.getCanonicalPath());
ProcessBuilder pb = new ProcessBuilder();
pb.command("cmd.exe", "/C", "start", "outlook.exe",
"/eml", resultEmail.getCanonicalPath());
Process p = pb.start();
try {
p.waitFor();
} finally {
p.getErrorStream().close();
p.getInputStream().close();
p.getErrorStream().close();
p.destroy();
}
}
You'll have to handle clean up after the email client is closed.
You also have to think about the security implications of email messages being left on the file system.

Java Mail - Attachments not showing in email clients?

Ok so I'm having to alter some old code from another dev that he sent up for sending emails from our app with Java Mail. This has worked fine for a long time but now we are required to send pdf attachments as well.
So basically below, assume there is an object "mail" that has getters for the text and html messages as well as now a getter for the pdf filename to load from the filesystem and attach to the mail.
I've altered the below code where marked, so if there is a pdf to attach, load from filesystem and attach. I've tried to use the same structure as the previous code, although I suspect its not all required?
Multipart mp = new MimeMultipart("alternative");
// Create a "text" Multipart message
BodyPart textPart = new MimeBodyPart();
textPart.setContent(mail.getText(), "text/plain");
mp.addBodyPart(textPart);
// Create a "HTML" Multipart message
Multipart htmlContent = new MimeMultipart("related");
BodyPart htmlPage = new MimeBodyPart();
htmlPage.setContent(mail.getHtml(), "text/html; charset=UTF-8");
htmlContent.addBodyPart(htmlPage);
BodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(htmlContent);
mp.addBodyPart(htmlPart);
// NEW CODE STARTS HERE
if(StringUtils.isNotBlank(mail.getPdfAttachmentFileName())) {
Multipart pdfContent = new MimeMultipart("mixed"); //<---- this appears to be an issue???
BodyPart pdfPage = new MimeBodyPart();
File file = new File(uploadDir + "/" + mail.getPdfAttachmentFileName());
DataSource dataSource = new ByteArrayDataSource(new FileInputStream(file), "application/pdf");
pdfPage.setDataHandler(new DataHandler(dataSource));
pdfPage.setFileName(mail.getPdfAttachmentFileName());
pdfContent.addBodyPart(pdfPage);
BodyPart pdfPart = new MimeBodyPart();
pdfPart.setContent(pdfContent);
mp.addBodyPart(pdfPart);
}
// NEW CODE ENDS HERE
mimeMessage.setContent(mp);
At any rate, the above works, sort of. There are no errors or exceptions and the message gets sent. BUT the attachment doesn't appear depending on which email client you recieve the mail with.
With the code as above, Outlook receives the message as readable and the attachment is visible and downloadable. This is perfect. BUT in GMail, the message is still readable, the paperclip appears to indicate there is an attachment, but there is no attachment to download?
If you switch the `Multipart pdfContent = new MimeMultipart("mixed");' to be "related" rather than "mixed" the exact opposite is true. GMail receives it perfectly but Outlook only gets the message and paperclip, no actual attachment.
Obviously we need to be sending emails to our customers with no knowledge of their email client used to open them! Obviously I'm a novice at Java Mail so have simply copied suggested code but this isn't gelling well with our existing code!
Any ideas how to alter the above to make it completely email client independant?
Ok turns out Spring has a helper class to hide all this mess away from you.
I've refactored all of the above code into the following and it works great;
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8");
message.setTo(mail.getTo());
message.setFrom(mail.getFrom());
message.setSubject(mail.getSubject());
message.setText(mail.getText(), mail.getHtml());
if(StringUtils.isNotBlank(mail.getPdfAttachmentFileName())) {
File file = new File(uploadDir + "/" + mail.getPdfAttachmentFileName());
DataSource dataSource = new ByteArrayDataSource(new FileInputStream(file), "application/pdf");
message.addAttachment(mail.getPdfAttachmentFileName(), dataSource);
}

Categories