33

I want to send an email with an inline image using javamail.

I'm doing something like this.

MimeMultipart content = new MimeMultipart("related");

BodyPart bodyPart = new MimeBodyPart();
bodyPart.setContent(message, "text/html; charset=ISO-8859-1");
content.addBodyPart(bodyPart);

bodyPart = new MimeBodyPart();
DataSource ds = new ByteArrayDataSource(image, "image/jpeg");
bodyPart.setDataHandler(new DataHandler(ds));
bodyPart.setHeader("Content-Type", "image/jpeg; name=image.jpg");
bodyPart.setHeader("Content-ID", "<image>");
bodyPart.setHeader("Content-Disposition", "inline");
content.addBodyPart(bodyPart);

msg.setContent(content);

I've also tried

    bodyPart.setHeader("inline; filename=image.jpg");

and

    bodyPart.setDisposition("inline");

but no matter what, the image is being sent as an attachment and the Content-Dispostion is turning into "attachment".

How do I send an image inline in the email using javamail?

Jops
  • 22,535
  • 13
  • 46
  • 63
akula1001
  • 4,576
  • 12
  • 43
  • 56

8 Answers8

38

Your problem

As far as I can see, it looks like the way you create the message and everything is mostly right! You use the right MIME types and everything.

I am not sure why you use a DataSource and DataHandler, and have a ContentID on the image, but you need to complete your question for me to be able to troubleshoot more. Especially, the following line:

bodyPart.setContent(message, "text/html; charset=ISO-8859-1");

What is in message? Does it contains <img src="cid:image" />?

Did you try to generate the ContentID with String cid = ContentIdGenerator.getContentId(); instead of using image


Source

This blog article taught me how to use the right message type, attach my image and refer to the attachment from the HTML body: How to Send Email with Embedded Images Using Java


Details

Message

You have to create your content using the MimeMultipart class. It is important to use the string "related" as a parameter to the constructor, to tell JavaMail that your parts are "working together".

MimeMultipart content = new MimeMultipart("related");

Content identifier

You need to generate a ContentID, it is a string used to identify the image you attached to your email and refer to it from the email body.

String cid = ContentIdGenerator.getContentId();

Note: This ContentIdGenerator class is hypothetical. You could create one or inline the creation of content IDs. In my case, I use a simple method:

import java.util.UUID;

// ...

String generateContentId(String prefix) {
     return String.format("%s-%s", prefix, UUID.randomUUID());
}

HTML body

The HTML code is one part of the MimeMultipart content. Use the MimeBodyPart class for that. Don't forget to specify the encoding and "html" when you set the text of that part!

MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setText(""
  + "<html>"
  + " <body>"
  + "  <p>Here is my image:</p>"
  + "  <img src=\"cid:" + cid + "\" />"
  + " </body>"
  + "</html>" 
  ,"US-ASCII", "html");
content.addBodyPart(htmlPart);

Note that as a source of the image, we use cid: and the generated ContentID.

Image attachment

We can create another MimeBodyPart for the attachment of the image.

MimeBodyPart imagePart = new MimeBodyPart();
imagePart.attachFile("resources/teapot.jpg");
imagePart.setContentID("<" + cid + ">");
imagePart.setDisposition(MimeBodyPart.INLINE);
content.addBodyPart(imagePart);

Note that we use the same ContentID between < and > and set it as the image's ContentID. We also set the disposition to INLINE to signal that this image is meant to be displayed in the email, not as an attachment.

Finish message

That's it! If you create an SMTP message on the right session and use that content, your email will contain an embedded image! For instance:

SMTPMessage m = new SMTPMessage(session);
m.setContent(content);
m.setSubject("Mail with embedded image");
m.setRecipient(RecipientType.TO, new InternetAddress("your@email.com"));
Transport.send(m)

Let me know if that works for you! ;)

snooze92
  • 4,178
  • 2
  • 29
  • 38
  • `attachFile` is not a method of `MimeBodyPart`, according to my IDE. I found an alternative solution: `DataSource fds = new FileDataSource("teapot.jpg"); messageBodyPart.setDataHandler(new DataHandler(fds));`. – Marcel Korpel May 26 '15 at 17:41
  • Found it, but still unsatisfactory: `attachFile` is part of JavaMail >= 1.4; however, I'm using 1.5.3, tested it with separate parts (mailapi-1.5.3.jar and smtp-1.5.3.jar) as well as complete API (javax.mail-1.5.3.jar), but `attachFile` is not available. – Marcel Korpel May 26 '15 at 19:20
  • I just checked, and I am seeing `attachFile` as a method of [`MimeBodyPart` in version 1.4.7](http://www.atetric.com/atetric/javadoc/javax.mail/javax.mail-api/1.4.7/src-html/javax/mail/internet/MimeBodyPart.html). I just looked and it seems to also be there [in version 1.5.2](http://www.atetric.com/atetric/javadoc/javax.mail/javax.mail-api/1.5.2/src-html/javax/mail/internet/MimeBodyPart.html). I cannot find sources online for a version 1.5.3 :( – snooze92 May 27 '15 at 22:18
  • 1
    hello, what jar u install for ContentIdGenerator? – Aza Suhaza Mar 15 '18 at 08:50
  • Hey @AzaSuhaza, sorry that is unclear in my initial answer. ContentIdGenerator is an hypothetical class. In my case, I use `java.util.UUID` like this `UUID.randomUUID()`. – snooze92 Mar 15 '18 at 12:57
  • Excellent explanation! Just wanted to add that this answer applies to MultiPartEmail of *Apache Commons Email*. Just do the same steps explained by @snooze92 and add your MimeMultipart object to your MultiPartEmail instance. – saygley Mar 19 '21 at 15:45
11

Why don't you try something like this?

    MimeMessage mail =  new MimeMessage(mailSession);

    mail.setSubject(subject);

    MimeBodyPart messageBodyPart = new MimeBodyPart();

    messageBodyPart.setContent(message, "text/html");

    Multipart multipart = new MimeMultipart();
    multipart.addBodyPart(messageBodyPart);

    messageBodyPart = new MimeBodyPart();
    DataSource source = new FileDataSource(new File("complete path to image.jpg"));
    messageBodyPart.setDataHandler(new DataHandler(source));
    messageBodyPart.setFileName(fileAttachment.getName());
    messageBodyPart.setDisposition(MimeBodyPart.INLINE);
    multipart.addBodyPart(messageBodyPart);

    mail.setContent(multipart);

in the message, have a <img src="image.jpg"/> tag and you should be ok.

Good Luck

Greg Leaver
  • 801
  • 6
  • 10
Bernardo
  • 111
  • 1
  • 3
  • 5
    Having the img tag in the body of the message is important. If your mail client does not recognize the image as being used in the body, it will display it as an attachment. – Zeki Jan 04 '11 at 14:53
  • I have exactly this same problem, could you please give me tips how i should write the img tag to avoid being displayed only as an attachment? Have a look at this post: http://stackoverflow.com/questions/5260654/embedding-images-into-html-email-with-java-mail/5261025#5261025 – javing Mar 10 '11 at 15:47
  • 1
    What is `fileAttachment` and where does it come from? – BairDev Oct 20 '21 at 17:10
10

This worked for me:

  MimeMultipart rootContainer = new MimeMultipart();
  rootContainer.setSubType("related"); 
  rootContainer.addBodyPart(alternativeMultiPartWithPlainTextAndHtml); // not in focus here
  rootContainer.addBodyPart(createInlineImagePart(base64EncodedImageContentByteArray));
  ...
  message.setContent(rootContainer);
  message.setHeader("MIME-Version", "1.0");
  message.setHeader("Content-Type", rootContainer.getContentType());

  ...


  BodyPart createInlineImagePart(byte[] base64EncodedImageContentByteArray) throws MessagingException {
    InternetHeaders headers = new InternetHeaders();
    headers.addHeader("Content-Type", "image/jpeg");
    headers.addHeader("Content-Transfer-Encoding", "base64");
    MimeBodyPart imagePart = new MimeBodyPart(headers, base64EncodedImageContentByteArray);
    imagePart.setDisposition(MimeBodyPart.INLINE);
    imagePart.setContentID("&lt;image&gt;");
    imagePart.setFileName("image.jpg");
    return imagePart;
Eric
  • 6,563
  • 5
  • 42
  • 66
Jörg Pfründer
  • 101
  • 1
  • 3
  • 4
    Would you please post the whole code or write the file and the method in which we need to put the provided code? Thanks –  Sep 13 '11 at 07:19
  • You need not inline & base encode - you can attach traditionally and add the link to the file in the message text as answered by @Bernardo – Ujjwal Singh Dec 12 '13 at 08:21
  • However remember to set the Header's Content-Type to image/jpg or so right before appending to the main message (after you have attached the file) – Ujjwal Singh Dec 17 '13 at 08:34
  • After my initial post, we learned that the base64 inline image part should be "chunked" base64. Some mail servers with aggressive virus scanner refused to deliver our mails with normal base64 images. – Jörg Pfründer Jan 06 '15 at 18:50
  • @Ujjwal Singh: In our case the image source was a base64 encoded inline image in html, so we did not think about converting it into a "traditional" file. We used html with inline base64 images, because it was easier for us to check the layout of the resulting email just by dumping the html string to a file and open it with firefox. – Jörg Pfründer Jan 06 '15 at 19:02
3

If you are using Spring use MimeMessageHelper to send email with inline content (References).

Create JavaMailSender bean or configure this by adding corresponding properties to application.properties file, if you are using Spring Boot.

@Bean
public JavaMailSender getJavaMailSender() {
    JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
    mailSender.setHost(host);
    mailSender.setPort(port);
    mailSender.setUsername(username);
    mailSender.setPassword(password);
    Properties props = mailSender.getJavaMailProperties();
    props.put("mail.transport.protocol", "smtp");
    props.put("mail.smtp.auth", authEnable);
    props.put("mail.smtp.starttls.enable", starttlsEnable);
    //props.put("mail.debug", "true");
    mailSender.setJavaMailProperties(props);
    return mailSender;
}

Create algorithm to generate unique CONTENT-ID

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Random;

public class ContentIdGenerator {

    static int seq = 0;
    static String hostname;

    public static void getHostname() {
        try {
            hostname = InetAddress.getLocalHost().getCanonicalHostName();
        }
        catch (UnknownHostException e) {
            // we can't find our hostname? okay, use something no one else is
            // likely to use
            hostname = new Random(System.currentTimeMillis()).nextInt(100000) + ".localhost";
        }
    }

    /**
     * Sequence goes from 0 to 100K, then starts up at 0 again. This is large
     * enough,
     * and saves
     * 
     * @return
     */
    public static synchronized int getSeq() {
        return (seq++) % 100000;
    }

    /**
     * One possible way to generate very-likely-unique content IDs.
     * 
     * @return A content id that uses the hostname, the current time, and a
     *         sequence number
     *         to avoid collision.
     */
    public static String getContentId() {
        getHostname();
        int c = getSeq();
        return c + "." + System.currentTimeMillis() + "@" + hostname;
    }

}

Send Email with inlines.

@Autowired
private JavaMailSender javaMailSender;

public void sendEmailWithInlineImage() {
    MimeMessage mimeMessage = null;
    try {
        InternetAddress from = new InternetAddress(from, personal);
        mimeMessage = javaMailSender.createMimeMessage();
        mimeMessage.setSubject("Test Inline");
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setFrom(from);
        helper.setTo("test@test.com");
        String contentId = ContentIdGenerator.getContentId();
        String htmlText = "Hello,</br> <p>This is test with email inlines.</p><img src=\"cid:" + contentId + "\" />";
        helper.setText(htmlText, true);

        ClassPathResource classPathResource = new ClassPathResource("static/images/first.png");
        helper.addInline(contentId, classPathResource);
        javaMailSender.send(mimeMessage);
    }
    catch (Exception e) {
        LOGGER.error(e.getMessage());
    }

}
Ankur Mahajan
  • 3,396
  • 3
  • 31
  • 42
3

RFC Specification could be found here(https://www.rfc-editor.org/rfc/rfc2392).

Firstly, email html content needs to format like : "cid:logo.png" when using inline pictures, see:

<td style="width:114px;padding-top: 19px">
    <img src="cid:logo.png" />
</td>

Secondly, MimeBodyPart object needs to set property "disposition" as MimeBodyPart.INLINE, as below:

String filename = "logo.png"
BodyPart image = new MimeBodyPart();
image.setDisposition(MimeBodyPart.INLINE);
image.setFileName(filename);
image.setHeader("Content-ID", "<" +filename+">");

Be aware, Content-ID property must prefix and suffix with "<" and ">" perspectively, and the value off filename should be same with the content of src of img tag without prefix "cid:"

Finally the whole code is below:

Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("1234@gmail.com");
InternetAddress[] recipients = { "123@gmail.com"};
msg.setRecipients(Message.RecipientType.TO, recipients);
msg.setSubject("for test");
msg.setSentDate(new Date());

BodyPart content = new MimeBodyPart();
content.setContent(<html><body>  <img src="cid:logo.png" /> </body></html>, "text/html; charset=utf-8");
String fileName = "logo.png";
BodyPart image = new MimeBodyPart();
image.setHeader("Content-ID", "<" +fileName+">");
image.setDisposition(MimeBodyPart.INLINE);
image.setFileName(fileName);
InputStream stream = MailService.class.getResourceAsStream(path);
DataSource fds = new ByteArrayDataSource(IOUtils.toByteArray(stream), "image/png");
image.setDataHandler(new DataHandler(fds));

MimeMultipart multipart = new MimeMultipart("related");
multipart.addBodyPart(content);
multipart.addBodyPart(getImage(image1));
msg.setContent(multipart);
msg.saveChanges();
Transport bus = session.getTransport("smtp");
bus.connect("username", "password");
bus.sendMessage(msg, recipients);
bus.close();
Community
  • 1
  • 1
Jackie Yu
  • 95
  • 11
2

I had some problems having inline images displayed in GMail and Thunderbird, made some tests and resolved with the following (sample) code:

String imagePath = "/path/to/the/image.png";
String fileName = imagePath.substring(path.lastIndexOf('/') + 1);
String htmlText = "<html><body>TEST:<img src=\"cid:img1\"></body></html>";
MimeMultipart multipart = new MimeMultipart("related");
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(htmlText, "text/html; charset=utf-8");
multipart.addBodyPart(messageBodyPart);
messageBodyPart = new MimeBodyPart();
DataSource fds = new FileDataSource(imagePath);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID", "<img1>");
messageBodyPart.setDisposition(MimeBodyPart.INLINE);
messageBodyPart.setFileName(fileName);
multipart.addBodyPart(messageBodyPart);
message.setContent(multipart);

Just some things to notice:

  • the "Content-ID" has to be built as specified in the RFCs (https://www.rfc-editor.org/rfc/rfc2392), so it has to be the part in the img tag src attribute, following the "cid:", enclosed by angle brackets ("<" and ">")
  • I had to set the file name
  • no need for width, height, alt or title in the img tag
  • I had to put the charset that way, because the one in the html was being ignored

This worked for me making inline images display for some clients and in GMail web client, I don't mean this will work everywhere and forever! :)

(Sorry for my english and for my typos!)

Community
  • 1
  • 1
debbbole
  • 21
  • 3
1

Below is the complete Code

    import java.awt.image.BufferedImage;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;

    private BodyPart createInlineImagePart()  {
    MimeBodyPart imagePart =null;
    try
    {

        ByteArrayOutputStream baos=new ByteArrayOutputStream(10000);
        BufferedImage img=ImageIO.read(new File(directory path,"sdf_email_logo.jpg"));
        ImageIO.write(img, "jpg", baos);
        baos.flush();

        String base64String=Base64.encode(baos.toByteArray());
        baos.close();

        byte[] bytearray = Base64.decode(base64String);
        InternetHeaders headers = new InternetHeaders();
        headers.addHeader("Content-Type", "image/jpeg");
        headers.addHeader("Content-Transfer-Encoding", "base64");
        imagePart = new MimeBodyPart(headers, bytearray);
        imagePart.setDisposition(MimeBodyPart.INLINE);
        imagePart.setContentID("&lt;sdf_email_logo&gt;");
        imagePart.setFileName("sdf_email_logo.jpg");
    }
    catch(Exception exp)
    {
        logError("17", "Logo Attach Error : "+exp);
    }

    return imagePart;
}


MimeMultipart mp = new MimeMultipart();
 //mp.addBodyPart(createInlineImagePart());

mp.addBodyPart(createInlineImagePart());

String body="<img src=\"cid:sdf_email_logo\"/>"
9ilsdx 9rvj 0lo
  • 7,955
  • 10
  • 38
  • 77
0

Use the following snippet:

MimeBodyPart imgBodyPart = new MimeBodyPart();
imgBodyPart.attachFile("Image.png");
imgBodyPart.setContentID('<'+"i01@example.com"+'>');
imgBodyPart.setDisposition(MimeBodyPart.INLINE);
imgBodyPart.setHeader("Content-Type", "image/png");

multipart.addBodyPart(imgBodyPart);

You need not in-line & base encode - you can attach traditionally and add the link to the main message's text which is of type text/html.
However remember to set the imgBodyPart's header's Content-Type to image/jpg or so right before appending to the main message (after you have attached the file).

Ujjwal Singh
  • 4,908
  • 4
  • 37
  • 54