Multipart Alternative + Multipart Related Email with ActionMailer
I had a need at work to send email out of our Rails application which met the following criteria:
- It has both a plain text and HTML part.
- It may have embedded images in the HTML part.
- It may have attachments.
Below is the code, which you can put in your app/models directory.
class EmbeddedMailer < ActionMailer::Base
def embedded_mail(to, from, subject, html_message, text_message, files = {}, embeds = {})
# Hack TMail to allow embeds
TMail::HeaderField::FNAME_TO_CLASS.delete 'content-id'
recipients to
from from
subject subject
content_type 'multipart/mixed'
# The Message
part :content_type => 'multipart/alternative' do |m|
# Plain Text Message
m.part :content_type => 'text/plain' do |p|
p.transfer_encoding = 'base64'
p.body = text_message
end
# HTML Message
m.part :content_type => 'multipart/related' do |x|
x.part :content_type => 'text/html' do |p|
p.body = html_message
end
# Embedded
embeds.each do |key, embed|
x.part :content_type => embed[:content_type] do |p|
p.headers = { 'Content-ID' => "<#{key}>" }
p.transfer_encoding = 'base64'
p.body = File.read embed[:path]
end
end
end
end
# Attachments
files.each do |key, embed|
attachment :content_type => embed[:content_type] do |a|
a.filename = key
a.body = File.read embed[:path]
end
end
end
end
A Quick Explanation
You have to hack TMail, the built in email parser for ActionMailer, so that it doesn’t try to validate the Content-ID header.
To meet all of the criteria above, you have to create a MIME-type hierarchy as follows:
- multipart/mixed – a mix of a message and attachments
- multipart/alternative – to mark that the text and html parts are equivalent to each other
- text/plain – the plain text section
- multipart/related – the HTML section with embedded images
- text/html – the HTML section
- Embedded images go here
- attachment/content-type – attachments are at the bottom
The format of the files and embeds hashes is as follows. The key is the file name of the attached file. The value is another hash with content_type set to the content type of the attachment and path is the full path to the file. For example:
{ 'image.png' =>
'content_type' => 'image/png',
'path' => '/tmp/image.png'
}
Update 2010-11-30: Fixed the layout to add multipart/mixed for attachments.
Update 2011-04-07: I have created a solution for Rails 3, which is much more simplistic.

