Search code examples
javagmailattachmentemail-attachmentsmime

Create a draft with PDF file as attachment using Gmail API in Java


I'm trying to create a draft with an attachment in Java using Gmail API.

Creating a basic draft works, so I've eliminated any permission problem. I've used code here as an inspiration but cannot get it to work.

Here's what I did so far:

public String generateGmailDraft(EmailRequestDto emailRequestDto, String quoteId) 
        throws MessagingException, IOException, GeneralSecurityException {
    // The attachment is a PDF file
    AttachmentDto lastQuoteData = getLastQuotePdfData(quoteId);

    MimeMessage email = createMimeMessage(emailRequestDto, lastQuoteData);
    Message messageWithEmail = createMessageWithEmail(email);

    Draft draft = new Draft();
    draft.setMessage(messageWithEmail);

    // this works
    Gmail gmail = gmail(googleGmailCredentialProperties);

    log.debug("attempting to send mail");
    draft = gmail.users().drafts().create(emailRequestDto.getFrom(), draft).execute();

    log.debug("Draft id: {}", draft.getMessage().getId());
    log.debug(draft.toPrettyString());

    return draft.getMessage().getId();
}


From Google example, I created a MIME message, but this is probably where the problem lies:

/**
 * Create a MimeMessage using the parameters provided
 * @return the MimeMessage to be used to send email
 * @throws MessagingException
 */
private MimeMessage createMimeMessage(EmailRequestDto requestDto, AttachmentDto attachment)
        throws MessagingException, IOException {
    Properties props = new Properties();
    Session session = Session.getDefaultInstance(props, null);

    MimeMessage email = new MimeMessage(session);
    email.setFrom(new InternetAddress(requestDto.getFrom()));
    email.addRecipient(javax.mail.Message.RecipientType.TO, ew InternetAddress(requestDto.getTo()));
    email.setSubject(requestDto.getSubject());

    BodyPart messageBodyPart = new MimeBodyPart();
    messageBodyPart.setHeader("Content-Type", "multipart/alternative");
    messageBodyPart.setText(requestDto.getBody());  // Setting the actual message

    // Create a multipart message and set text message part
    Multipart multipart = new MimeMultipart();
    multipart.addBodyPart(messageBodyPart);

    // Try to add an attachment
    MimeBodyPart attachmentBP = new MimeBodyPart();
    attachmentBP.setFileName(attachment.fileName);

    ByteArrayOutputStream baos = attachmentToByteArrayOutputStream(attachment.file);
    DataSource dataSrc = new ByteArrayDataSource(baos.toByteArray(), "application/pdf");
    attachmentBP.setDataHandler(new DataHandler(dataSrc));
    multipart.addBodyPart(attachmentBP);

    // Send the complete message parts
    email.setContent(multipart);

    return email;
}


This private class hopefully provides everything needed for the attachment, when used, all private fields are correctly filled.

/**
AttachmentDTO returns everything needed to create an attachment
- file
- mimeype
- filename
- inputstream : used because file can come from different sources in the app.
*/ 
private static final class AttachmentDto {

    private final InputStream inputStream;
    private final String fileName;
    private File file;
    private String mimeType;

    private AttachmentDto(InputStream inputStream, String fileName) {
        this.inputStream = inputStream;
        this.fileName = fileName;

        File outputFile = new File(fileName);
        try (OutputStream out = new FileOutputStream(outputFile)) {
            IOUtils.copy(inputStream, out);
            file = outputFile;
            mimeType = Files.probeContentType(outputFile.toPath());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public InputStream getInputStream() {
        return inputStream;
    }

    public String getFileName() {
        return fileName;
    }

    public File getFile() {
        return file;
    }

    public String getMimeType() {
        return mimeType;
    }
}


Current state:

  • code gets stuck on gmail.users().drafts().create(emailRequestDto.getFrom(), draft).execute(); and never returns
  • following the link above, I also tried to encode the attachment as Base64, then use

    public Gmail.Users.Drafts.Create create( String userId, Draft content, AbstractInputStreamContent mediaContent) throws java.io.IOException

    which is, I suppose, the counterpart to use when you have an attachment.

  • When I use this, draft gets created in my mailbox but without the attachment.

Any help appreciated, thank you.


Solution

  • Try something similar to this:

    private MimeMessage createEmailWithAttachment(String to,
            String subject,
            String bodyText,
            File file) throws MessagingException, IOException {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);
    
        MimeMessage email = new MimeMessage(session);
    
        email.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to));
        email.setSubject(subject);
    
        MimeBodyPart mimeBodyPart = new MimeBodyPart();
        mimeBodyPart.setContent(bodyText, "text/plain");
    
        Multipart multipart = new MimeMultipart();
        multipart.addBodyPart(mimeBodyPart);
    
        mimeBodyPart = new MimeBodyPart();
        DataSource source = new FileDataSource(file);
    
        mimeBodyPart.setDataHandler(new DataHandler(source));
        mimeBodyPart.setFileName(file.getName());
    
        multipart.addBodyPart(mimeBodyPart);
        email.setContent(multipart);
    
        return email;
    }