Unable to render inline attachments in ActionMailer - ruby-on-rails

I'm trying to render inline images in a standard ActionMailer view using the approach outlined here Rails attachments inline are not shown correctly in gmail.
My code in my mailer:
attachments.inline["sample.png"] = {
mime_type: "image/png",
encoding: "base64", # Including this line causes byte sequence error
data: File.read('public/sample.png')
}
In mail view:
image_tag(attachments["sample.png"].url)
Gives the familiar ruby UTF-8 byte sequence error:
invalid byte sequence in UTF-8
To get around this I tried the following:
attachments.inline["logo.png"] = {
mime_type: "image/png",
data: Base64.encode64(File.read('public/logo.png'))
}
and also
attachments.inline["logo.png"] = File.read('public/logo.png')
Using the same image_tag syntax shown above.
Both of these resolve the UTF error, but I'm left with this nonsensical URL in the view:
<img src="cid:5707a64ededbc_7bd83ffd648601e029875#localhostname.mail">
The PNG image is valid and renders properly in a standard HTML view. I'm using Rails 4.2.5 with Ruby 2.2.4
EDIT
This works:
Mailer:
attachments.inline["cape.png"] = {
mime_type: "image/png",
# encoding: "base64",
content: Base64.encode64(File.read(Rails.root.join("public/", "cape.png")))
}
View:
= image_tag "data:image/png;base64,#{attachments['logo.png'].read}"
Very awkward, however, and I'm still wondering why the conventional approach doesn't work.

In my application I use only
attachments.inline["logo.png"] = File.read('public/logo.png')
It works fine for me

Related

Attaching AXLSX view to mail Rails Mailer

According to the GitHub Page for the axlsx gem I should use this syntax to render a xlsx view to a file and attach it:
xlsx = render_to_string handlers: [:axlsx], formats: [:xlsx], template: "users/export", locals: {users: users}
attachments["Users.xlsx"] = {mime_type: Mime::XLSX, content: xlsx}
Here is my mail method:
xlsx = render_to_string(handlers: [:axlsx], formats: [:xlsx], template: 'v1/reports/reportxyz', params: {start_date: '2016-09-12', period: 'weekly'})
attachments["report.xlsx"] = {content: xlsx, mime_type: Mime::XLSX}
mail(to: "my#email.address", subject: "Report", format: "text")
However I get this error when I try and call the mailer method:
ActionView::MissingTemplate: Missing template layouts/mailer with {:locale=>[:en], :formats=>[:xlsx], :variants=>[], :handlers=>[:axlsx]}. Searched in:
* "path/to/project/app/views"
Why is the render_to_string method affecting what the mailer view the mailer is trying to render? locgially I don't have a mailer.xlsx.axlsx file in my app/views/layouts folder but rather the mailer.text.erb I am trying to use as with other emails.
EDIT
I changed the render line to xlsx = render_to_string(template: 'v1/reports/azamara_social', params: {start_date: '2016-09-12', period: 'weekly'})
And now it seems to try and render the xlsx view but of course gets nil:NilClass errors when the xlsx view tries to reference instance variables defined in the reports controller.
Have you tried passing layout: false? What versions of axlsx, axlsx_rails, rails, and rubyzip are you using?
In the end it all came down to moving the controller code into a lib file. This way I call it in the controller to get the data if it needs to be rendered via web-requests as well as via the Mailer method where I recreate the #variables the view template is looking for.
Here is the finished salient parts of the report mailer method:
data = ReportUtils.get_data(args)
xlsx = render_to_string(template: 'path/to/report.xlsx', locals: {:#period => period, :#date_ranges => data[:date_ranges], :#data => data[:data]})
attachments["report.xlsx"] = {content: xlsx, mime_type: Mime::XLSX}

Rails saving a pdf to Amazon S3

I have a Rails 3.2 app that uses gem 'wicked_pdf', and gem 'combine_pdf'.
They both work and I can create PDFs which get emailed.
But, I have run into a situation where the email would be too big.
So, I'm trying to save the created pdf to Amazon S3. The app already has the gem 'aws-sdk'.
This is my code:
def self.saveallpdf
#costprojects = Costproject.where("client_id = 2")
pdf = CombinePDF.new
#costprojects.each do |costproject|
#costproject = costproject
controller = CostprojectsController.new
controller.instance_variable_set(:"#costproject", #costproject)
pdf2 = controller.render_to_string(pdf: "Captital Projects.pdf",
template: "costprojects/viewproject",
encoding: "UTF-8")
pdf << CombinePDF.parse(pdf2)
end
#s3 = AWS::S3.new
#bucket = #s3.buckets['ndeavor3-pdf']
#obj = #bucket.objects['filename'].write(pdf, acl: :public_read)
end
The error I'm getting is:
:data must be provided as a String, Pathname, File, or an object that responds to #read and #eof?
/app/vendor/bundle/ruby/1.9.1/gems/aws-sdk-1.8.3.1/lib/aws/s3/data_options.rb:125:in `validate_data!'
/app/vendor/bundle/ruby/1.9.1/gems/aws-sdk-1.8.3.1/lib/aws/s3/data_options.rb:32:in `compute_write_options'
/app/vendor/bundle/ruby/1.9.1/gems/aws-sdk-1.8.3.1/lib/aws/s3/s3_object.rb:594:in `write'
/app/app/models/costproject.rb:167:in `saveallpdf
'
I guess was-sdk doesn't like the "pdf" as the file??
PS - I can email the "pdf" - if it was smaller in size.
Thanks for your help!
At this point in time:
#obj = #bucket.objects['filename'].write(pdf, acl: :public_read)
pdf is a CombinePDF object and not a File, String, or Pathname
pdf.to_s might work, or you will have to create a new file from the CombinePDF object
File.new(CombinePDF) # pseduo code only

Generating and sending PDF in ActionMailer with WickedPDF

i am using rails 4.2 and am generating pdfs in actionmailer with following code:
attachments["abc.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string(template: "pdf_templates/abc.html", header: {
content: render_to_string(layout: "header.html")
}, margin: {
top: 50, left: 50
})
)
mail to: #user.email, subject: "bla blubb"
Its working fine and rendering the abc.html.erb. but it ignores margin-tags and the header file... if i put an error into header.html.erb rails is shouting - so it must be found and processed.
i also tried this but same issue:
pdf = render template: "pdf_templates/abc.html", footer: {spacing: 20, left: "ABDCDSAFASDF"}
attachments["abc.pdf"] = WickedPdf.new.pdf_from_string(pdf)
i read about some problems with ActionMailer but cant solve them, cause i am using rails 4:
wicked_pdf not loading header or footer in ActionMailer
Rails3 - wicked_pdf gem, footer issue when calling from action mailer model
Funny, thought that i tried the include which unixMonkey recommend. Now I tried again and it works with following code... Thank you for pushing me into the right direction.
class WelcomeMailer < ApplicationMailer
include PdfHelper
helper(MailerHelper)
def new_customer(user)
#user = user
test = render_to_string_with_wicked_pdf(
pdf: "test.pdf",
template: "pdf_templates/abc.html",
header: {html: {template: "pdf_templates/header.html"}}
)
attachments["abc.pdf"] = test
mail to: #user.email, subject: "blaa blubb"
end
end

Prevent Ruby ActionMailer from removing carriage returns (Windows line endings) from txt file attachment

I'm trying to send a file attachment using Rubys ActionMailer. However, when I send my file, the carriage returns "\r" that I've added are removed.
string = "the quick brown\r\nfox jumped over\r\nthe bridge"
File.open(file = "attachment_#{Time.now.to_i}.txt", "w+") do |f|
f.write(string)
end
attachments['test_file.txt'] = {
mime_type: 'text/plain',
content: string
}
mail(
:to => 'somebody#example.com',
:from => 'somebody#example.com',
:subject => 'Message Test'
).deliver
The file that is written has the proper line endings, but the attached file has the carriage returns removed. How I can prevent this from happening?
So just wanted to post my solution in case anyone else ends up with this issue...
After checking the base64 encoded attachment from the email, I found that the string, did in fact, not have the carriage return.
1.9.3-p448 :001 > Base64.decode64('dGhlIHF1aWNrIGJyb3duCmZveCBqdW1wZWQgb3Zlcgp0aGUgYnJpZGdlCg==')
=> "the quick brown\nfox jumped over\nthe bridge\n"
This led me to believe that the ActionMailer was in fact reformatting my email before it was encoded. I figured that I could just encode the message body manually and send it over ....
encoded = Base64.encode64(string)
attachments['test_file.txt'] = {
mime_type: 'text/plain;charset=utf-8',
encoding: 'base64',
content: encoded
}
And that seems to have done the trick. My attachment now contains carriage return and line feed endings ("\r\n")
I'm not sure if this is expected functionality for the ActionMailer. I definitely didn't expect it.

Rails send_data throws "invalid byte sequence in UTF-8"... but why?

I'm using Rails to generate a PDF with the executable wkhtmltopdf and then using send_data to send the result back to the user as a PDF file.
view = ActionView::Base.new(ActionController::Base.view_paths, {})
html = "<h1>A heading</h1>"
pdfdata = `echo '#{html}' | #{RAILS_ROOT}/lib/pdf/wkhtmltopdf-i386 - -`
send_data pdfdata, :filename => 'readthis.pdf', :disposition => 'attachment', :type => "application/pdf"
The PDF is generated properly, but Rails complains ArgumentError (invalid byte sequence in UTF-8) from the send_data method. Changing it to send "foobar" as :type => text/html makes it work, so it's definitely got a problem with pdfdata.
I don't understand. Isn't send_data supposed to send binary data? Of course it's not valid UTF-8. Or am I missing something?
Thanks
Rails assumes UTF-8. Telling it explicitly that it is binary data solves the problem. Thanks for your help.
pdfdata.force_encoding('BINARY')
Did you inspect the variable pdfdata and check whether it is proper or not?

Resources