'Add embedded image in emails in AWS SES service

I am trying to write a Java app which can send emails to specify emails. In the email i also want to attach some pic.

Please find my code below :-

public class AmazonSESSample {

    static final String FROM = "[email protected]";
    static final String TO = "[email protected]";
    static final String BODY = "This email was sent through Amazon SES by using the AWS SDK for Java. hello";
    static final String SUBJECT = "Amazon SES test (AWS SDK for Java)";

    public static void main(String[] args) throws IOException {
        Destination destination = new Destination().withToAddresses(new String[] { TO });
        Content subject = new Content().withData(SUBJECT);
        Message msg = new Message().withSubject(subject);
        // Include a body in both text and HTML formats
        //Content textContent = new Content().withData("Hello - I hope you're having a good day.");
        Content htmlContent = new Content().withData("<h2>Hi User,</h2>\n"
                + " <h3>Please find the ABC Association login details below</h3>\n"
                + " <img src=\"logo.png\" alt=\"Mountain View\">\n"
                + " Click <a href=\"http://google.com">here</a> to go to the association portal.\n"
                + " <h4>Association ID - 12345</h4>\n" + "  <h4>Admin UID - suny342</h4>\n"
                + " <h4>Password - poass234</h4>\n" + " Regards,\n" + " <br>Qme Admin</br>");
        Body body = new Body().withHtml(htmlContent);
        msg.setBody(body);
        SendEmailRequest request = new SendEmailRequest().withSource(FROM).withDestination(destination)
                .withMessage(msg);
        try {
            System.out.println("Attempting to send an email through Amazon SES by using the AWS SDK for Java...");
            AWSCredentials credentials = null;
            credentials = new BasicAWSCredentials("ABC", "CDF");
            try {
                // credentialsProvider.
            } catch (Exception e) {
                throw new AmazonClientException("Cannot load the credentials from the credential profiles file. "
                        + "Please make sure that your credentials file is at the correct "
                        + "location (/Users/iftekharahmedkhan/.aws/credentials), and is in valid format.", e);
            }
            AmazonSimpleEmailService client = AmazonSimpleEmailServiceClientBuilder.standard()
                    .withCredentials(new AWSStaticCredentialsProvider(credentials)).withRegion("us-west-2").build();
            client.sendEmail(request);
            System.out.println("Email sent!");
        } catch (Exception ex) {
            System.out.println("The email was not sent.");
            System.out.println("Error message: " + ex.getMessage());
        }
    }
}

The image is placed in the resource directory but it is not being embeded in the email. Can anyone please help.



Solution 1:[1]

Instead of relative path, you'll need to use either an absolute public path to the image itself or a data URL. For example:

<img src=\"https://example.com/logo.png\" alt=\"Mountain View\" />

or

<img src=\"data:image/png;base64, {BASE64_ENCODED_DATA}\" alt=\"Mountain View\" />

EDIT

As of January 2020, Gmail still does not support base64 encoded images.

Solution 2:[2]

The method posted by @sebagra works well.

In case of Python using boto3 and ses client, the way to set to set the Content-Disposition to inline is:

    att.add_header('Content-ID', '<myImage>')
    att.add_header('Content-Disposition', 'inline', filename=os.path.basename(IMAGE_PATH))

Full example based on the python example in the AWS docs:

    import os
    import boto3
    from botocore.exceptions import ClientError
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    from email.mime.application import MIMEApplication
    
    # Replace [email protected] with your "From" address.
    # This address must be verified with Amazon SES.
    SENDER = "Sender Name <[email protected]>"
    
    # Replace [email protected] with a "To" address. If your account 
    # is still in the sandbox, this address must be verified.
    RECIPIENT = "[email protected]"
    
    # Specify a configuration set. If you do not want to use a configuration
    # set, comment the following variable, and the 
    # ConfigurationSetName=CONFIGURATION_SET argument below.
    CONFIGURATION_SET = "ConfigSet"
    
    # If necessary, replace us-west-2 with the AWS Region you're using for Amazon SES.
    AWS_REGION = "us-west-2"
    
    # The subject line for the email.
    SUBJECT = "Customer service contact info"
    
    # The full path to the file that will be attached to the email.
    IMAGE_PATH = "path/to/myImage.png"
    
    # The email body for recipients with non-HTML email clients.
    BODY_TEXT = "Hello,\r\nPlease see the attached file for a list of customers to contact."
    
    # The HTML body of the email.
    BODY_HTML = """\
    <html>
    <head></head>
    <body>
    <h1>Hello!</h1>
    <p>Please see the attached file for a list of customers to contact.</p>
    </body>
    </html>
    """
    
    # The character encoding for the email.
    CHARSET = "utf-8"
    
    # Create a new SES resource and specify a region.
    client = boto3.client('ses',region_name=AWS_REGION)
    
    # Create a multipart/mixed parent container.
    msg = MIMEMultipart('mixed')
    # Add subject, from and to lines.
    msg['Subject'] = SUBJECT 
    msg['From'] = SENDER 
    msg['To'] = RECIPIENT
    
    # Create a multipart/alternative child container.
    msg_body = MIMEMultipart('alternative')
    
    # Encode the text and HTML content and set the character encoding. This step is
    # necessary if you're sending a message with characters outside the ASCII range.
    textpart = MIMEText(BODY_TEXT.encode(CHARSET), 'plain', CHARSET)
    htmlpart = MIMEText(BODY_HTML.encode(CHARSET), 'html', CHARSET)
    
    # Add the text and HTML parts to the child container.
    msg_body.attach(textpart)
    msg_body.attach(htmlpart)
    
    # Define the attachment part and encode it using MIMEApplication.
    att = MIMEApplication(open(IMAGE_PATH, 'rb').read())
    
    # Add a header to tell the email client to treat this part as an attachment,
    # and set an id and content disposition.
    att.add_header('Content-ID', '<myImage>')
    att.add_header('Content-Disposition', 'inline', filename=os.path.basename(IMAGE_PATH))
    
    # Attach the multipart/alternative child container to the multipart/mixed
    # parent container.
    msg.attach(msg_body)
    
    # Add the attachment to the parent container.
    msg.attach(att)

    try:
        response = client.send_raw_email(
            Source=SENDER,
            Destinations=[
                RECIPIENT
            ],
            RawMessage={
                'Data': msg.as_string(),
            }
        )
    # Display an error if something goes wrong. 
    except ClientError as e:
        print(e.response['Error']['Message'])
    else:
        print("Email sent! Message ID:"),
        print(response['MessageId'])

In case of using sesv2 the msg is built the same but the the api to use is send_email:

    ...
    client = boto3.client('sesv2',region_name=AWS_REGION)
    ...
    response = client.send_email(
        FromEmailAddress=SENDER,
        Destination={
            'ToAddresses': [
                RECIPIENT
            ]
        },
        Content={
            'Raw': {
                'Data': msg.as_string()
            }
        }
    )
    ...

Solution 3:[3]

I was able to send an email using AWS SES with images that can be seen in the GMail client, by attaching the images to the message and using an inline disposition reference to them.

I used the code explained in the AWS docs to attach images to a MimeMessage, and then using the cid reference from the HTML to those images (as explained in this post answer).

First, we attach the images to the message adding a couple of specific attributes (Header and Disposition):

        MimeMultipart msg = new MimeMultipart("mixed");        
        DataSource fds = new FileDataSource("/path/to/my/image.png");
        att.setDataHandler(new DataHandler(fds));
        att.setFileName(fds.getName());         
        att.setHeader("Content-ID","<myImage>");
        att.setDisposition("inline; filename=\"image.png\"");
        msg.addBodyPart(att);

Note that the < and > in the Content-ID attribute must be present enclosing whatever id you choose (myImage in my example).

Then, in the HTML of the message body we just need to add the cid (content id) of each image:

<img src="cid:myImage">

For the full code, I pretty much used the AWS reference above (using same variable names), the only changes made were the ones of the setHeader and setDisposition methods.

Solution 4:[4]

I had no trouble sending an inline base 64 image to a Yahoo account using AWS SES. When I tried to send to a GMail account I had trouble. The text I sent rendered, but the image didn't show.

I discovered that GMail wasn't stripping the image. It was just not displaying it. I confirmed this by selecting More -> "Show original" while viewing the message in GMail.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Dharman
Solution 4 Chuck Smith