问题
I have been searching for ages and I still cant seem to figure this out,
I am currently using the prawn gem
and I want to be able to attach a pdf
to an email
in my invoice controllers
create action
. I am currently able to link to a generated pdf from my invoices/show
page through http://localhost:3000/invoices/297.pdf
but I cant figure out how to attach this pdf to an email.
Currently I am not storing the generated PDF anywhere and my mailer looks like this
Mailer
class InvoiceMailer < ActionMailer::Base
default :from => "notifications@example.com"
def invoice_email(user)
@user = user
mail(:to => user.email, :subject => "Invoice Recieved")
end
end
And my InvoicesController Create Action
{...}
respond_to do |format|
if @invoice.save
InvoiceMailer.invoice_email(@invoice.user).deliver
format.html { redirect_to invoice_url(@invoice, back_path:
{...}
How can I add my invoice as an attachment to this mailer? Do I need to store the invoice somewhere before I can send it?
回答1:
How you do this depends on how long the PDF generation takes and/or how much load it places on your server and whether you care about that. In my case I was generating PDFs from user-generated-content and I was seeing some PDF creation times up in the 30+ seconds range. Solving for that becomes a run-the-job-somewhere-else and cache-it (whether DB or cloud storage) issue.
@toddmetheny is quite right in suggesting cloud storage for all but the simplest solutions. It gets more interesting if you are hosting on something like Heroku with ephemeral storage, or if you are separating PDF creation from email sending from user requests (e.g. from Heroku again, web dynos vs worker dynos). You can generate the PDF to a local temporary file, but that temporary file may not be there by the time you need to read it in your Mailer running on a 'worker'.
Really simple option
In your Mailer you could generate the PDF to a local file, read it back into memory, then attach it:
def invoice_email(user)
@user = user
attachments['filename_for_user.pdf'] = generate_pdf_content
mail(:to => user.email, :subject => "Invoice Recieved")
end
private
# I had troubles trying to get Prawn to generate 'in memory'
# so just write to, then read, then unlink a tempfile
def generate_pdf_content
pdf = some_method_that_generates_a_prawn_pdf_object
Tempfile.create do |f|
pdf.render_file f
f.flush
File.read(f)
end
end
I suggest you start here to get things working.
More complicated option
Someday you may want to separate the job that generates the PDF (which can take a long time) from the jobs that send email, which are usually much faster. The way I do this is to have a job that generates the PDF to a local Tempfile, then uploads that file to S3 storage and records the S3 object id (I do it in my database, you could just do it as a job attribute you push around).
When complete, that job creates a new mailer job. The mailer job (which may execute on a different server) downloads the PDF from S3 storage to a local file, then adds it to the email much like the simple option above.
回答2:
You'll need a url you can work with. Any cloud storage solution is an option if you don't want to store it on your database.
Here's some pertinent info on adding attachments to mailers from rails guides on action mailer:
2.3.1 Adding Attachments
Action Mailer makes it very easy to add attachments.
Pass the file name and content and Action Mailer and the Mail gem will automatically guess the mime_type, set the encoding and create the attachment.
attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
When the mail method will be triggered, it will send a multipart email with an attachment, properly nested with the top level being multipart/mixed and the first part being a multipart/alternative containing the plain text and HTML email messages.
来源:https://stackoverflow.com/questions/36491009/attach-prawn-pdf-to-email