How to Send Email with Embedded Images Using Java

Many applications need to send e-mail messages, for everything from server status updates to marketing messages, and some of these include photos, charts, or other images. You certainly have received plenty of email that contains images, but you may not know programmatically how to send such a thing from your Java code. In this article, I show the basics of using JavaMail, how to send HTML messages, and how to use MIME to create a multipart message that encloses the images, either as attachments, or as part of the message body.

Send Email with Embedded Images Using Java

The easiest way to send email with Java is by using a library provided by Oracle. There are a few jar files in the distribution. The simplest thing to do is to use mail.jar, since it contains the whole API and several transport implementations. Another jar file contains only the API, and Oracle broke down the implementations for each transport into its own jar file. You can pick-and-choose to suit your application’s needs. For example, if you send email from an applet, you would only include mailapi.jar and smtp.jar.

Note that some Java frameworks have features that make it even easier to send email. For example, the Spring Framework includes an API that provides a layer of abstraction over JavaMail. The Play Framework, on the other hand, provides a plugin to send email.

So, your very first step, even before writing a line of code, is to make sure you have the mail.jar or the various jar files you chose in your classpath, and take whatever other steps are required to make sure to include the jar file (or files) in your deployment process.

Creating a simple email message

MimebodypartThe class that represents an email message in JavaMail is, unsurprisingly, javax.mail.Message. This is an abstract class, so we have to hunt down the specific subclass we need. Message only has one direct subclass in the API, javax.mail.internet.MimeMessage. You’d think it’s good news, since it’s not even abstract, but upon closer observation, you will discover that none of the constructors are public. So, we have to find another subclass to instantiate.

But first, why do we even want a MimeMessage?

Once upon a time, all you could send through email was plain text, and only 7-bit text at that. Now, thanks to MIME, the Multipurpose Internet Mail Extensions standard, we can send just about anything (short of chocolate cake). Email messages can contain multiple parts; each part can encode data of a different type. The message itself tells the recipient’s client how to split up the message data into parts, and how to treat each part.

Thus: We need to create a MimeMessage. Each transport has its own Message subclass, so we need to figure out which transport to use to send the message. The JavaMail API supports three widely used email protocols: SMTP, POP3, and IMAP. Out of those, only SMTP is a protocol specifically designed for sending messages; the other two are used to retrieve messages from a server, so the choice is obvious.

So, lets’ get coding.

public static Message buildSimpleMessage(Session session)

  throws MessagingException {

  SMTPMessage m = new SMTPMessage(session);

  MimeMultipart content = new MimeMultipart();

  MimeBodyPart mainPart = new MimeBodyPart();

  mainPart.setText("Hello there! This is simple demo message");

  content.addBodyPart(mainPart);

  m.setContent(content);

  m.setSubject("Demo message");

  return m;

}

To create the Message, we need a javax.mail.Session object, which I discuss in a moment. Here, we create a Message and set its content to a MimeMultipart, the holder for the MIME parts. Then we create a single MIME part to hold a short bit of text.

Sending the message

To send the message, we need to configure a Session object using the SMTP protocol. If you are using a server that doesn’t require authentication or access through TLS/SSL, you can simply give the transport, host, and sender:

  public static Session buildSimpleSession() {

    Properties mailProps = new Properties();

    mailProps.put("mail.transport.protocol", "smtp");

    mailProps.put("mail.host", "localhost");

    mailProps.put("mail.from", "example@example.com");

    return Session.getDefaultInstance(mailProps);

  }

In many situations, sending email through a company mail server requires TLS/SSL, authentication, or both. TLS is Transport Layer Security, the successor to SSL, which is Secure Socket Layer. These protocols use an encrypted connection to prevent eavesdropping. The protocols cannot guarantee that it is impossible to break the encryption, but with the current state of technology, it is unlikely.

So, for this example, we use Gmail SMTP to send the message, since Gmail uses both, and is a popular mail handling system. 

To support authentication, we need to implement an Authenticator. The code below creates an anonymous subclass of Authenticator. It implements getPasswordAuthentication by returning a PasswordAuthentication object that we created in the buildGoogleSession method. Notice that the PasswordAuthentication has to be final so that it can be pulled into the context of the Authenticator.

We tell the transport to use TLS by setting the property mail.smtp.starttls.enable to true. You can confirm that it uses TLS by turning on debugging for the session (session.setDebug(true)), and looking at the transcript when sending a message. Gmail says, “250-STARTTLS” during the initial exchange, confirming that it is using TLS.

public static Session buildGoogleSession() {

  Properties mailProps = new Properties();

  mailProps.put("mail.transport.protocol", "smtp");

  mailProps.put("mail.host", "smtp.gmail.com");

  mailProps.put("mail.from", "example@gmail.com");

  mailProps.put("mail.smtp.starttls.enable", "true");

  mailProps.put("mail.smtp.port", "587");

  mailProps.put("mail.smtp.auth", "true");

  // final, because we're using it in the   closure below

  final PasswordAuthentication usernamePassword =

    new PasswordAuthentication("example@gmail.com", "someReallyGoodPassword");

  Authenticator auth = new Authenticator() {

    protected PasswordAuthentication getPasswordAuthentication() {

      return usernamePassword;

    }

  };

  Session session = Session.getInstance(mailProps, auth);

  session.setDebug(true);

  return session;

}

Now that we have a message, and we’ve said how that message is to be sent, let’s send it.

There are two ways to send a message. Either we tell the message where to go, or we tell the transport where to send the message. So, any of the three options below works:

// using setRecipient

message.setRecipient(RecipientType.TO, new InternetAddress("example@gmail.com"));

Transport.send(message);

 

// using setRecipients

InternetAddress[] addresses = {new InternetAddress("example@gmail.com")};

message.setRecipients(RecipientType.TO, addresses);

Transport.send(message);

 

// using send(Message, InternetAddress)

InternetAddress[] addresses = {new InternetAddress("example@gmail.com")};

Transport.send(message, addresses);

There is one important difference among these options: If you use setRecipient() or setRecipients(), the message itself contains the recipient’s email address. This is normal when you send the message to a single user, and thus fine if your application is sending, say, a notice to a subscriber from the public library that the copy of Best Chocolate Recipes she put on hold is ready for pickup. But if the message is sent  to multiple users, every recipient can see who else received the message. Only you can tell if this is an issue in your application. You can use other RecipientTypes if you want to set the CC or BCC field of your message.

Adding an attachment

By adding an attachment, the message becomes a true multipart message, where the first part is the text and the second part is the picture, or document, or any file you want to send. Do give some consideration to attachment size, just as you do when you send email manually. Some mail servers have message size limits; while your mail server may accept larger attachments, the path to your recipient could involve servers that are more restrictive. Also, binary files need to be encoded, which results in added overhead. To be safe, keep your attachments under about 10M, or 7M for binary data like images.

The MimeBodyPart class has a superbly convenient method to add the contents of a file to a message, attachFile(String) or attachFile(File). To use it, we create a second MimeBodyPart, and attach the file to it. Then we add the body part to the message content. 

Here is how we attach the file teapot.png in the resources directory. 

MimeBodyPart imagePart = new MimeBodyPart();

imagePart.attachFile("resources/teapot.jpg");

content.addBodyPart(imagePart);

You can then send the message just as you did before.

If the user downloads or saves the attachment, the proposed name is the same as the name of the file you sent.

If the file does not exist, you get an error (a NullPointerException) when the software tries to send the message, not when building it. Depending on the application logic, you should probably check for the existence of the file when attaching it, rather than rely on an exception being thrown later. Since the file could still be deleted between the time you attach it an the time you send the message, always handle exceptions responsibly.

Sending email as HTML

Sending mail as HTML is just a matter of telling the MimeBodyPart that it’s what you’re doing. Instead of sending plain text, build your message as HTML, and set the content type to text/html.

The convenience method, setText(text, characterSet, subtype) wants to know the character set used to encode the HTML. If you send a message in English, “US-ASCII” should be sufficient. For other Western-European languages, “ISO-8859-1”, and for other languages, you have probably already had to deal with character sets and you know which one to use. 

textPart.setText("<html><head>"

          + "<title>The title is not usually displayed</title>"

          + "</head>n"

          + "<body><div><strong>Hi there!</strong></div>n"

          + "<div>Sending HTML in email is so <em>cool!</em></div>"

          + "</body></html>",

          "ascii", "html");

The HTML in your message can refer to images from a Web server. Do keep in mind that if you do not control the Web server, you cannot guarantee that the image will still be there in a month, or a year, so it may be more appropriate to send the image as part of the email message itself. You may also want to include the image in the email if it was generated for this single use, and you do not want to store a copy on your own servers forever.

Referring to an attachment from within the HTML

To tell the mail client to display the attached file within the message body, we have to send the message as HTML, and ensure that the HTML refers to the image that’s in the message itself. But what’s the URL of the image? It’s not on a server; it’s in the message!

The MIME specification allows us to identify message parts using the header Content-ID. HTML in one part of the message can refer to another part of the message with URLs that use the scheme cid:. Content IDs should be unique across all messages, so you may want to use a generator that uses the host name of the computer sending the message, the current time, and a sequence number to avoid possible collision. Practically, however, many programmers don’t bother following the specs and just use an id they find meaningful. Email clients seem to handle it without trouble.

When you set the ContentID on the MimeBodyPart, you must include angle brackets (< and >) around the content id. JavaMail does not do it for you, even though the angle brackets are part of the specification for the MIME Content-ID header.

The html part is built using:

String cid = generateCID()

MimeBodyPart textPart = new MimeBodyPart();

textPart.setText("<html><head>"

   + "<title>This is not usually displayed</title>"

   + "</head>n"

   + "<body><div><strong>Hi there!</strong></div>"

   + "<div>Sending HTML in email is so <em>cool!</em> </div>n"

   + "<div>And here's an image: <img src="cid:"

   + cid

   + "" /></div>n" + "<div>I hope you like it!</div></body></html>",

   "US-ASCII", "html");

content.addBodyPart(textPart);

and the image part with

MimeBodyPart imagePart = new MimeBodyPart();

imagePart.attachFile("resources/teapot.jpg");

imagePart.setContentID("<" + cid + ">");

imagePart.setDisposition(MimeBodyPart.INLINE);

content.addBodyPart(imagePart);

Notice how we tell mail clients that the image is to be displayed inline (not as an attachment) with getDisposition().

Lastly, we also tell the mail client that the text part and the image part are related and should be shown as a single item, not as separate pieces of the message. We do so by changing how we create the message content:

   MimeMultipart content = new MimeMultipart("related");

When you send that message and read it in an email client that supports text/html mail, you see the teapot right in the middle of the message.

You can view the complete code at https://github.com/nancyd/emaildemo.

About the author:

Nancy Deschênes has been developing for the Web for more than 15 years. In that time, she has worn many hats, acting at times as a front-end developer, database specialist, and (her favorite) application architect. She has used various technologies, mostly Java and the Spring MVC framework, but has recently spent most of her time using the Grails framework. She is the technical co-founder of myTurn.com, a platform for online rental of physical goods.

See also:






 







Comments

  1. MukeshKoshyM says:

    Excellent article

  2. Nice Article.. I hope this will solve my problem..

  3. Nice article. Thanks!

  4. Nice article, gotta try to embed images to my email.

Speak Your Mind

*