3
votes

I've taken a look at similar posts that mostly deal with sending an attachment by creating a view and controller, such as:

PDF attachment in email is called 'Noname'

but I've got a process that generates files in the background dynamically and need to attach it to a recipient list using ActionMailer::Base.mail. Below is the code:

def send_email(connection)
    email = ActionMailer::Base.mail(to: connection['to'], from: connection['from'], subject: 'Sample File', body: "<p>Hello,</p><p>Your data is ready</p>", content_type: 'multipart/mixed')
    email.cc = connection['cc'] if connection['cc'].present?
    email.bcc = connection['bcc'] if connection['bcc'].present?
    @files.each do |file|
      report_file_name = "#{@start_time.strftime('%Y%M%dT%I%m%s')}_#{file[0]}.xlsx"
      file_location = "#{Rails.root}/tmp/#{report_file_name}"
      email.attachments[report_file_name] = File.open(file_location, 'rb'){|f| f.read}
    end
    email.deliver if email
  end

I can see in the logs that it's sending with the content but assume it's sending as Noname because it can't find the view. Any way to get this to work successfully?

Below is the sample output:

Sent mail to [email protected] (383.9ms) Date: Thu, 13 Oct 2016 08:47:30 -0400 From: Sample To: Recipient Message-ID: <[email protected]> Subject: Sample File Mime-Version: 1.0 Content-Type: multipart/mixed; charset=UTF-8 Content-Transfer-Encoding: 7bit

-- Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; filename=20161012T08101476259208_Data.xlsx Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=20161012T08101476259208_Data.xlsx Content-ID: <[email protected]>

UEsDBBQAAAAIAO.. ... ...ADUFQAAAAA=

Update - I noticed if I use email.content_type = 'text/plain' - the attachment comes through successfully. For me, this works, though I'd appreciate later being able to style my emails with HTML

I presume this works because it prevents Rails from its usual gleaning/autointerpreting process. I'd certainly like to see a multipart/mixed or html compatible version work here though.

Update 2 This only fixed the issue artificially in the rails_email_preview gem, which renders the emails to a new tab in development. In production, this simply and understandably prints the details and the presumably base64-encoded file, so question remains open.

1
I am having the same problem now? Any news about how to fix it?Jimmy Huang

1 Answers

0
votes

I have meet this problem too, after some investigation, it seems in Rails 4, you can't call attachments method after calling mail method, otherwise the content_type of the mail message object won't have boundary information so that the attachments part can't be parsed correctly in the received email.

I think digging into the actionmailer source code and you should be able to find a solution, either by override the default mail method or set the correct boundary info manually.

But for quick resolving this problem, I thought out a not elegant work around by using meta programming: define a delegation class which inherits ActionMailer::Base.

class AnyMailer < ActionMailer::Base
  # a delegation mailer class used to eval dynamic mail action
end

Then eval this class with defining an arbitrary method to perform the email sending.

def send_email(connection, files)
  AnyMailer.class_eval do
    def any_mailer(connection, files)
      files.each do |file|
        report_file_name = :foo
        file_location = :bar
        attachments[report_file_name] = File.open(file_location, 'rb'){|f| f.read}
      end
      mail(to: connection['to'], from: connection['from'], subject: 'Sample File', body: "<p>Hello,</p><p>Your data is ready</p>")
    end
  end

  AnyMailer.any_mailer(connection, files).deliver_now
end

Attention, you don't need to specify the content_type as 'multipart/mixed', ActionMailer will handle it correctly. I tried to specify it explicitly but get messed up email content instead.