我正在使用以下代码在rails中发送电子邮件:
class InvoiceMailer < ActionMailer::Base
def invoice(invoice)
from CONFIG[:email]
recipients invoice.email
subject "Bevestiging Inschrijving #{invoice.course.name}"
content_type "multipart/alternative"
part "text/html" do |p|
p.body = render_message 'invoice_html', :invoice => invoice
end
part "text/plain" do |p|
p.body = render_message 'invoice_plain', :invoice => invoice
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
对我来说似乎很好,电子邮件也会在Gmail网络界面中显示出来。但是,在Mail(Apple程序)中,我只得到1个附件(应该有2个附件)并且没有文本。我似乎无法弄清楚造成它的原因。
我从日志中复制了电子邮件:
Sent mail to xxx@gmail.com From: yyy@gmail.com To: xxx@gmail.com Subject: Bevestiging Inschrijving Authentiek Spreken Mime-Version: 1.0 Content-Type: multipart/alternative; boundary=mimepart_4a5b035ea0d4_769515bbca0ce9b412a --mimepart_4a5b035ea0d4_769515bbca0ce9b412a Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: Quoted-printable Content-Disposition: inlineDear sir
= --mimepart_4a5b035ea0d4_769515bbca0ce9b412a Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: Quoted-printable Content-Disposition: inline Dear sir * Foo= --mimepart_4a5b035ea0d4_769515bbca0ce9b412a Content-Type: application/pdf; name=factuur.pdf Content-Transfer-Encoding: Base64 Content-Disposition: attachment; filename=factuur.pdf JVBERi0xLjMK/////woxIDAgb2JqCjw8IC9DcmVhdG9yIChQcmF3bikKL1By b2R1Y2VyIChQcmF3bikKPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEK ... ... ... MCBuIAp0cmFpbGVyCjw8IC9JbmZvIDEgMCBSCi9TaXplIDExCi9Sb290IDMg MCBSCj4+CnN0YXJ0eHJlZgo4Nzc1CiUlRU9GCg== --mimepart_4a5b035ea0d4_769515bbca0ce9b412a Content-Type: application/pdf; name=Spelregels.pdf Content-Transfer-Encoding: Base64 Content-Disposition: attachment; filename=Spelregels.pdf JVBERi0xLjQNJeLjz9MNCjYgMCBvYmoNPDwvTGluZWFyaXplZCAxL0wgMjEx NjYvTyA4L0UgMTY5NTIvTiAxL1QgMjEwMDAvSCBbIDg3NiAxOTJdPj4NZW5k ... ... ... MDIwNzQ4IDAwMDAwIG4NCnRyYWlsZXINCjw8L1NpemUgNj4+DQpzdGFydHhy ZWYNCjExNg0KJSVFT0YNCg== --mimepart_4a5b035ea0d4_769515bbca0ce9b412a--
答案 0 :(得分:21)
我怀疑问题在于您将整个电子邮件定义为多部分/替代方案,建议每个部分只是同一消息的备用视图。
我使用以下内容发送带附件的混合html /普通电子邮件,似乎工作正常。
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|
part "text/html" do |p|
p.body = render_message 'invoice_html', :invoice => invoice
end
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
答案 1 :(得分:15)
@jcoleman是正确的,但如果你不想使用他的宝石,那么这可能是一个更好的解决方案:
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
此代码首先创建以下MIME层次结构:
multipart/related
multipart/alternative
text/html
text/plain
image/png
在致电mail
后,我们会创建一个新的multipart/related
部分,并添加现有部分的子项(在我们去的时候删除它们)。然后我们强制Content-Type
为multipart/mixed
并继续添加附件,以及生成的MIME层次结构:
multipart/mixed
multipart/related
multipart/alternative
text/html
text/plain
image/png
application/pdf
答案 2 :(得分:12)
向James致敬,因为它帮助我让我们的邮件工作正常。
稍微改进一下:首先,我们使用块中的块参数来添加部分(当我没有时,我遇到了问题)。
另外,如果要使用布局,则必须直接使用#render。这是两个原则在工作中的一个例子。如上所示,您需要确保将html部分保留在最后。
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
此示例使用选项哈希创建包含附件和布局的通用多部分邮件,您可以像这样使用:
TestMailer.deliver_message_with_attachment_and_layout(
:from => 'a@fubar.com', :to => 'b@fubar.com',
:subject => 'test', :render => 'test',
:attachment => { :filename => 'A Nice PDF',
:path => 'path/to/some/nice/pdf' } )
(我们实际上并没有这样做:让每个邮件程序为您填写很多这些详细信息更好,但我认为这样可以更容易理解代码。)
希望有所帮助。祝你好运。
此致 丹
答案 3 :(得分:12)
Rails 3以不同的方式处理邮件 - 虽然简单的情况更容易,但是使用备用内容类型和(内联)附件为多部分电子邮件添加正确的MIME层次结构相当复杂(主要是因为所需的层次结构非常复杂。)
菲尔的回答似乎有效 - 但由于MIME层次结构仍然不正确,因此iPhone(以及其他设备)上的附件不可见。
正确的MIME层次结构如下所示:
multipart/mixed
multipart/alternative
multipart/related
text/html
image/png
(例如,对于内联附件; pdf将是另一个好例子)text/plain
application/zip
(例如附件 - 非内联)我发布了一个有助于支持正确层次结构的gem: https://github.com/jcoleman/mail_alternatives_with_attachments
通常在使用ActionMailer 3时,您将使用以下代码创建一条消息:
class MyEmailerClass < ActionMailer::Base
def my_email_method(address)
mail :to => address,
:from => "noreply@myemail.com",
:subject => "My Subject"
end
end
使用此gem创建包含备选方案和附件的电子邮件,您将使用以下代码:
class MyEmailerClass < ActionMailer::Base
def my_email_method(address, attachment, logo)
message = prepare_message to: address, subject: "My Subject", :content_type => "multipart/mixed"
message.alternative_content_types_with_attachment(
:text => render_to_string(:template => "my_template.text"),
:html => render_to_string("my_template.html")
) do |inline_attachments|
inline_attachments.inline['logo.png'] = logo
end
attachments['attachment.pdf'] = attachment
message
end
end
答案 4 :(得分:5)
注意:我的技术在某些情况下适用。但是,当与内嵌图像结合使用时,会导致附件无法在iPhone Mail上显示,也可能在其他客户端上显示。请参阅下面的jcoleman答案以获得完整的解决方案。
值得注意的是,Rails现在处理这个问题,至少从 3.1rc4 开始。来自ActionMailer指南:
class UserMailer < ActionMailer::Base
def welcome_email(user)
@user = user
@url = user_url(@user)
attachments['terms.pdf'] = File.read('/path/terms.pdf')
mail(:to => user.email,
:subject => "Please see the Terms and Conditions attached")
end
end
诀窍是在调用mail
之前添加附件 - 在触发问题中提到的三个替代问题后添加附件。
答案 5 :(得分:4)
Rails 3解决方案,包含pdf附件的多部分替代电子邮件(html和普通版),无内联附件
以前我的电子邮件只显示了pdf附件,当它们在ios中打开时,或者在osx上的mail.app中都没有显示正文和html。 Gmail从来都不是问题。
我使用与Corin相同的解决方案,但我不需要内联附件。这让我走得很远 - 除了一个问题 - mail.app / iOS邮件显示纯文本不是html。这是(如果最终发生),因为替代部分的顺序通过,首先是html然后是文本(为什么这应该是决定性的,但是无论如何都要打败我。)
所以我不得不再做一次改变,相当愚蠢,但它确实有效。添加.reverse!方法
所以我有
def guest_notification(requirement, message)
subject = "Further booking details"
@booking = requirement.booking
@message = message
mixed = mail(:to => [requirement.booking.email], :subject => subject) do |format|
format.text
format.html
end
mixed.add_part(
Mail::Part.new do
content_type 'multipart/alternative'
# THE ODD BIT vv
mixed.parts.reverse!.delete_if {|p| add_part p }
end
)
mixed.content_type 'multipart/mixed'
mixed.header['content-type'].parameters[:boundary] = mixed.body.boundary
attachments['Final_Details.pdf'] = File.read(Rails.root + "public/FinalDetails.pdf")
end
答案 6 :(得分:3)
Rails 4解决方案
在我们的项目中,我们向客户发送包含公司徽标(内联附件)和PDF(常规附件)的电子邮件。我们采用的解决方法类似于@ user1581404提供的解决方法。
然而,在将项目升级到Rails 4之后,我们必须找到一个新的解决方案,因为在调用mail
命令后不再允许您添加附件。
我们的邮件程序有一个基本邮件程序,我们通过使用以下方法覆盖邮件方法来解决此问题:
def mail(headers = {}, &block)
message = super
# If there are no regular attachments, we don't have to modify the mail
return message unless message.parts.any? { |part| part.attachment? && !part.inline? }
# Combine the html part and inline attachments to prevent issues with clients like iOS
html_part = Mail::Part.new do
content_type 'multipart/related'
message.parts.delete_if { |part| (!part.attachment? || part.inline?) && add_part(part) }
end
# Any parts left must be regular attachments
attachment_parts = message.parts.slice!(0..-1)
# Reconfigure the message
message.content_type 'multipart/mixed'
message.header['content-type'].parameters[:boundary] = message.body.boundary
message.add_part(html_part)
attachment_parts.each { |part| message.add_part(part) }
message
end
答案 7 :(得分:2)
的 N.B。 Rails 3.2解决方案。
就像这些多部分电子邮件一样,这个答案有多个部分,所以我会相应地进行剖析:
@ Corin的“混合”方法中的plain / html格式的顺序非常重要。我发现html后面的文字给了我所需的功能。 YMMV
将Content-Disposition设置为nil(删除它)修复了iPhone / iOS附件查看其他答案中表达的难点。此解决方案已经过测试,适用于Outlook for Mac,Mac OS / X Mail和iOS Mail。我怀疑其他电子邮件客户端也可以正常工作。
与以前版本的Rails不同,附件处理与广告一样有效。我最大的问题通常是由尝试旧的解决方法引起的,这些解决方案只能为我解决问题。
希望这可以帮助别人避免我的陷阱和死胡同。
工作代码:
def example( from_user, quote)
@quote = quote
# attach the inline logo
attachments.inline['logo.png'] = File.read('./public/images/logo.png')
# attach the pdf quote
attachments[ 'quote.pdf'] = File.read( 'path/quote.pdf')
# create a mixed format email body
mixed = mail( to: @quote.user.email,
subject: "Quote") do |format|
format.text
format.html
end
# Set the message content-type to be 'multipart/mixed'
mixed.content_type 'multipart/mixed'
mixed.header['content-type'].parameters[:boundary] = mixed.body.boundary
# Set Content-Disposition to nil to remove it - fixes iOS attachment viewing
mixed.content_disposition = nil
end