Action Mailerでエクセルを添付する方法

環境

  • rails 3.2.8
  • ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.4.0]
  • actionmailer 3.2.8

rails3のaction mailerでメールを送る際にエクセルを添付したいケースがあると思う。

Rails Guideにならって書くとこうなる。

# -*- coding: utf-8 -*-

class TestMailer < ActionMailer::Base
  default from: "hakutoitoi@example.com"

  def test_email
    attachments['test.xls'] = File.read('/tmp/test.xls')
    
    mail(:to => "hakutoitoi@example.com",
         :subject => "a mail with excel")
  end
end

実行してみると、下記のようなエラーで失敗した。

$ rails console
[1] pry(main)> mail = TestMailer.test_email
ArgumentError: invalid byte sequence in UTF-8from /Users/hakutoitoi/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.2.8/lib/active_support/core_ext/object/blank.rb:105:in `=~'

普通エクセルはSHIFT-JISで作成されるがaction_mailerはattachmentsにUTF-8を期待している感じ。 色々調査した結果、下記のようにバイナリ指定をすることでマルチバイトを含んだエクセルを添付できました。

# -*- coding: utf-8 -*-

class TestMailer < ActionMailer::Base
  default from: "hakutoitoi@example.com"

  def test_email
    attachments['test.xls'] = {
      :content => File.read('/tmp/test.xls', :mode => 'rb'),
      :transfer_encoding => :binary
    }
    
    mail(:to => "hakutoitoi@example.com",
         :subject => "a mail with excel")
  end
end

再度実行してみると...

[1] pry(main)> mail = TestMailer.test_email
=> #<Mail::Message:70296836491860, Multipart: true, Headers: <From: hakutoitoi@example.com>, <To: hakutoitoi@example.com>, <Subject: a mail with excel>, <Mime-Version: 1.0>, <Content-Type: multipart/mixed; boundary="--==_mimepart_5093785b42888_1ae43fef40834cd410818"; charset=UTF-8>>

[2] pry(main)> mail.deliver
=> #<Mail::Message:70296836491860, Multipart: true, Headers: <Date: Fri, 02 Nov 2012 16:38:12 +0900>, <From: hakutoitoi@example.com>, <To: hakutoitoi@example.com>, <Message-ID: <5093786488979_1ae43fef40834cd411095@hakutoitoi.co.jp.mail>>, <Subject: a mail with excel>, <Mime-Version: 1.0>, <Content-Type: multipart/mixed; boundary="--==_mimepart_5093785b42888_1ae43fef40834cd410818"; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>>

成功した。

まとめ

  1. File.readでファイルを読む時にバイナリモードを指定する
  2. attachmentsメソッドのオプションとして :transer_encoding => :binary を指定する