I always had confusion on how to send both text and html with images content in single mail, so that all the mail clients which can render html will show html content and others which cannot render HTML will show the text content.
While sending any mail, the content-type of the mail is very important. Every content to be sent is attached to the MimeMessage as a BodyPart with proper Content type. i.e if we send a simple Text mail, content type would be text/plain, for a HTML mail it would be text/html. similarly if we send a HTML mail with embedded images, it would be multipart/relative message with each part specifying its content type.
When coming to sending both text and html content along with embedded messages ( will be referred as TEXT-HTML images embedded mail herewith ), its gets bit complex, which i want to discuss here. The order in which these BodyParts are attached to the MimeMessage is very important so that message gets rendered properly in all mail clients.
First we will look at creating the various body parts needed in sending a TEXT-HTML images embedded mail.
private BodyPart createTextBody(File textContentFile)
throws MessagingException, IOException
{
StringBuffer contentBuffer = new StringBuffer();
BufferedReader reader = null;
FileInputStream stream = null;
try {
stream = new FileInputStream(textContentFile);
reader = new BufferedReader(new InputStreamReader(stream, "utf-8"));
String content;
while ((content = reader.readLine()) != null) {
contentBuffer.append(content);
}
} finally {
if (reader != null) {
reader.close();
}
if (stream != null) {
stream.close();
}
}
BodyPart textMessageBodyPart = new MimeBodyPart();
textMessageBodyPart.setContent(contentBuffer.toString(), "text/plain; charset=UTF-8");
return textMessageBodyPart;
}
private BodyPart createHtmlBody(File htmlContentFile)
throws MessagingException, IOException
{
StringBuffer contentBuffer = new StringBuffer();
BufferedReader reader = null;
FileInputStream stream = null;
try {
stream = new FileInputStream(htmlContentFile);
reader = new BufferedReader(new InputStreamReader(stream, "utf-8"));
String content;
while ((content = reader.readLine()) != null) {
contentBuffer.append(content);
}
} finally {
if (reader != null) {
reader.close();
}
if (stream != null) {
stream.close();
}
}
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(contentBuffer.toString(), "text/html; charset=UTF-8");
return messageBodyPart;
}
private BodyPart createImagePart(File imagePath, String cid)
throws MessagingException
{
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(new DataHandler(new FileDataSource(imagePath)));
messageBodyPart.setHeader("Content-ID", "<" + cid + ">");
messageBodyPart.setDisposition("inline");
return messageBodyPart;
}
Attaching these contents to mimemessage, we have two options (actually its only one option, as other option will not render the email properly in some mail clients, which is the reason for this entire article)
Option one:
Create a multipart/alternative message, with two parts
- First part having the TextBodyPart.
- Second part will be a multipart/related message, again with two parts
- First part will have HTMLBodyPart
- Second part will be the ImageBodyPart to be Embedded which were used in HTMLBodyPart
How on arriving at the above multipart subtypes is beyond the scope of this entry. Interested users can get needed information here
Translating the above steps into code:
private void updateContentOptionOne(MimeMessage mimeMessage, File textContentFile,
File htmlContentFile, File imagePath)
throws MessagingException, IOException
{
MimeMultipart outerPart = new MimeMultipart("alternative");
BodyPart textBody = createTextBody(textContentFile);
outerPart.addBodyPart(textBody);
MimeMultipart innerPart = new MimeMultipart("related");
innerPart.addBodyPart(createHtmlBody(htmlContentFile));
innerPart.addBodyPart(createImagePart(imagePath, imagePath.getName()));
BodyPart relatedPart = new MimeBodyPart();
relatedPart.setContent(innerPart);
outerPart.addBodyPart(relatedPart);
mimeMessage.setContent(outerPart);
}
If the above sent content is checked in Yahoo! Mail, Gmail or on offline web clients like Thnderbird, Evolution everything looks fine. but if you use a webmail Client which displays only textContent like SquirrelMail, you will see only the text content. and there would be no option to view the html content as shown below:
Coming to the Option two:
Create a multipart/related message, with two parts
- First part will be a multipart/alternative message, again with two parts
- First part will have TextBodyPart
- Second part having the HTMLBodyPart
- Second part will be the ImageBodyPart to be Embedded which are used in HTMLBodyPart
Translating the above steps into code:
private void updateContentOptionTwo(MimeMessage mimeMessage, File textContentFile,
File htmlContentFile, File imagePath)
throws MessagingException, IOException
{
MimeMultipart outerPart = new MimeMultipart("related");
MimeMultipart innerPart = new MimeMultipart("alternative");
innerPart.addBodyPart(createTextBody(textContentFile));
innerPart.addBodyPart(createHtmlBody(htmlContentFile));
BodyPart alternativePart = new MimeBodyPart();
alternativePart.setContent(innerPart);
outerPart.addBodyPart(alternativePart);
BodyPart imagePart = createImagePart(imagePath, imagePath.getName());
outerPart.addBodyPart(imagePart);
mimeMessage.setContent(outerPart);
}
Now if you check the mail sent in SquirrelMail WebMailClient, it looks like below, text content with html mail as an attachment.
Clicking on the attachment will show the HTML content.
Again in this option (Option Two), the order in which we attach the alternative messages are important, as in some offline mail clients like Thunderbird (tested on version 2.0.0.14), it picks up the last alternative message in the list of available alternatives it can render. So if we attach the HtmlBodypart before the TextBodyPart, we would be seeing the Text content, though Thunderbird can render the HTML content attached.

Download Source
[...]