Problem sending multipart mail using ActionMailer
I suspect the issue is you're defining the overall email as multipart/alternative, suggesting each part is just an alternate view of the same message.
I use something like the following to send mixed html/plain emails with attachments, and it seems to work OK.
class InvoiceMailer < ActionMailer::Base
def invoice(invoice)
from CONFIG[:email]
recipients invoice.email
subject "Bevestiging Inschrijving #{invoice.course.name}"
content_type "multipart/mixed"
part(:content_type => "multipart/alternative") do |p|
p.part "text/html" do |p|
p.body = render_message 'invoice_html', :invoice => invoice
end
p.part "text/plain" do |p|
p.body = render_message 'invoice_plain', :invoice => invoice
end
end
pdf = Prawn::Document.new(:page_size => 'A4')
PDFRenderer.render_invoice(pdf, invoice)
attachment :content_type => "application/pdf", :body => pdf.render, :filename => "factuur.pdf"
invoice.course.course_files.each do |file|
attachment :content_type => file.content_type, :body => File.read(file.full_path), :filename => file.filename
end
end
end
@jcoleman is correct but if you don't want to use his gem then this may be a better solution:
class MyEmailerClass < ActionMailer::Base
def my_email_method(address, attachment, logo)
# Add inline attachments first so views can reference them
attachments.inline['logo.png'] = logo
# Call mail as per normal but keep a reference to it
mixed = mail(:to => address) do |format|
format.html
format.text
end
# All the message parts from above will be nested into a new 'multipart/related'
mixed.add_part(Mail::Part.new do
content_type 'multipart/related'
mixed.parts.delete_if { |p| add_part p }
end)
# Set the message content-type to be 'multipart/mixed'
mixed.content_type 'multipart/mixed'
mixed.header['content-type'].parameters[:boundary] = mixed.body.boundary
# Continue adding attachments normally
attachments['attachment.pdf'] = attachment
end
end
This code begins by creating the following MIME hierarchy:
multipart/related
multipart/alternative
text/html
text/plain
image/png
After the call to mail
we create a new multipart/related
part and add the children of the existing part (removing them as we go). Then we force the Content-Type
to be multipart/mixed
and continue adding attachments, with the resulting MIME hierarchy:
multipart/mixed
multipart/related
multipart/alternative
text/html
text/plain
image/png
application/pdf
A nod to James on this, as it helped me get our mailer working right.
A slight refinement to this: First, we use the block arguments within the blocks to add parts (I had problems when I didn't).
Also, if you want to use layouts, you have to use #render directly. Here's an example of both principles at work. As shown above, you need to make sure you keep the html part last.
def message_with_attachment_and_layout( options )
from options[:from]
recipients options[:to]
subject options[:subject]
content_type "multipart/mixed"
part :content_type => 'multipart/alternative' do |copy|
copy.part :content_type => 'text/plain' do |plain|
plain.body = render( :file => "#{options[:render]}.text.plain",
:layout => 'email', :body => options )
end
copy.part :content_type => 'text/html' do |html|
html.body = render( :file => "#{options[:render]}.text.html",
:layout => 'email', :body => options )
end
end
attachment :content_type => "application/pdf",
:filename => options[:attachment][:filename],
:body => File.read( options[:attachment][:path] + '.pdf' )
end
This example uses an options hash to create a generic multipart message with both attachments and layout, which you would use like this:
TestMailer.deliver_message_with_attachment_and_layout(
:from => '[email protected]', :to => '[email protected]',
:subject => 'test', :render => 'test',
:attachment => { :filename => 'A Nice PDF',
:path => 'path/to/some/nice/pdf' } )
(We don't actually do this: it's nicer to have each mailer fill in a lot of these details for you, but I thought it would make it easier to understand the code.)
Hope that helps. Best of luck.
Regards, Dan