download pdf from S3 through active admin on rails - ruby-on-rails

I have a PDF uploaded in order_items table through paperclip gem. PDF is uploaded successfully and I can see the file uploaded by visiting the S3 url generated.
My problem is, when I am downloading file on active admin it is giving me error:
ActionController::MissingFile
Cannot read file
My member_action in active admin is:
member_action :art_proof, method: :get do
#order = resource
#order_item = #order.order_items.where(id: params[:item_id]).first
#uniform = #order_item.uniform
#stock = #order_item.stock
if #order_item.decoration_preview.url
send_file #order_item.decoration_preview.url,
filename: #order_item.decoration_preview_file_name,
type: #order_item.decoration_preview_content_type
render :nothing => true
else
render layout: false
end
end
I am trying to download file through send_file method. Any idea why this is happening?

I don't think you can use send_file with an S3 URL. Instead I pre-sign the URL and redirect_to the URL. Something like:
aws_resource = Aws::S3::Resource.new(credentials: credentials)
presigner = Aws::S3::Presigner.new(client: aws_resource.client)
redirect_to presigner.presigned_url(:get_object, bucket: bucket_name, key: file.key)

Related

Double rendering error in Rails while using wicked pdf gem

In my rails app, I am creating an API that accepts order details from other devices and generates pdf which is then uploaded to the AWS.I am using wicked_pdf gem to generate the pdf and aws-sdk to upload data to Aws. The controller code is as follows.
def order_invoice
response = Hash.new
result = Hash.new
if params[:order] && params[:order][:txnid] &&
params[:no_of_copies] && params[:order][:total_amount]!= 0
#order = params[:order]
...
#no_of_copies = params[:no_of_copies]
invoice = create_pdf
response['result'] = invoice
response.merge! ApiStatusList::OK
else
response.merge! ApiStatusList::INVALID_REQUEST
end
render :json => response
end
def create_pdf
pdf = WickedPdf.new.pdf_from_string(
render_to_string(template: 'invoices/generate_invoice.pdf.erb'))
send_data(pdf, filename: params[:order][:txnid] + ".pdf" ,
type: 'application/pdf',
disposition: 'attachment', print_media_type: true)
save_path = Rails.root.join('pdfs', #order['txnid'] + ".pdf")
File.open(save_path, 'wb') do |file|
file << pdf
filename = #order['txnid'] + ".pdf"
end
file_name = #order['txnid'] + ".pdf"
upload = Invoice.upload(save_path, file_name)
end
While generating and uploading the pdf I get the following error
AbstractController::DoubleRenderError in Api::V0::InvoiceApiController#order_invoice
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
I need to give the link of the uploaded pdf as response. I know the error is because of using two renders here. However I dont know how to overcome the error. Can anyone help me to tweak and correct the code. Newbie in rails and api's.
This is because you render repsonse 2 times:
via send_data in #create_pdf
via render :json => response in action
UPD:
After discussion, we came to this - you don't need content of the file in your response, only link to AWS.
So, to fix this just remove your send_file call:
def create_pdf
# render_to_string doesn`t mean "render response",
# so it will not end up "double render"
pdf = WickedPdf.new.pdf_from_string(
render_to_string(template: 'invoices/generate_invoice.pdf.erb'))
# send_file renders response to user, which you don't need
# this was source of "double render" issue
# so we can just remove it
save_path = Rails.root.join('pdfs', #order['txnid'] + ".pdf")
File.open(save_path, 'wb') do |file|
file << pdf
filename = #order['txnid'] + ".pdf"
end
file_name = #order['txnid'] + ".pdf"
upload = Invoice.upload(save_path, file_name)
end

How to upload image in ruby app folder and insert url in database column

Controller
Save the object
How to Use This Controller to insert image in any folder and image url store in database
def create_json
#user = User.new(userFirstName: params[:userFirstName], userLastName: params[:userLastName], userEmail: params[:userEmail], password: encrypted_password, userImage: params[:userImage])
if #user.save #if save succeeds, redirect to the index action
redirect_to(:action => 'show', id: User.last.id)
else
#if not succeeds, redirect to the index action
redirect_to(:action => 'new')
end end
User(Model) mount_uploader :userImage, AvatarUploader
UsersController -> #user = User.new(user_params)
if #user.save
redirect_to(:action => 'show', id: User.last.id)
else
render :json => data_hash2, :content_type => 'application/json'
end
class AvatarUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(jpg jpeg gif png)
end end
If You Using Web Service
<form action="http://ruby/controllername/create" lass="new_user" id="new_user" enctype="multipart/form-data" accept-charset="UTF-8" method="post">
View following method for uploading files to app/assets/images/people_profile_images folder
First upload Image like this:
#user =User.new(user_params)
#this will create user form paramaters given
uploaded_file = params[:user][:photo]
#this will add uploaded image or file in uplaoded_fie
if (uploaded_file)
File.open(Rails.root.join('app/assets', 'images/people_profile_images', uploaded_file.original_filename), 'wb') do |file|
file.write(uploaded_file.read)
#user.photo = "/assets/people_profile_images/"+uploaded_file.original_filename
end
else
#user.photo = "/assets/people_profile_images/default_avatar.png"
end
then save user
#user.save
I don't know of an approach that can cover both: image uploading and then saving the url on the database.
You can do one or the other:
1) Use a gem called paperclip that helps upload image files.
See this links:
https://github.com/thoughtbot/paperclip#ruby-and-rails
https://teamtreehouse.com/library/image-uploads-in-ruby-on-rails-41 (paperclip, carrierwave, and dragonfly)
I have used paperclip and it has worked well for me.
OR:
2) Include it as an additional field on your filename.html.erb for your image_url
One caveat, of using this though is that this can be changed and you have no control of the image that might show on your site. Thus, the image_url should be from a reputable CDN or at least from a reputable user uploading to your site.
Create a column url:string in your model. Add it as a hidden field in your form. Set its value to the same as your remote_url using .js
var urladdy = input.remoteurl.val()
input.url.val(urladdy)
This will upload image with carrier wave and also save its url as string.

How to save generated pdf in server in rails

Currently I am generating a pdf using prawn gem of rails. pdf is generated when user hit on this action
def print_pdf
#user = User.find(params[:id])
#details = #user.details_data
respond_to do |format|
format.pdf do
pdf = PrintDetailsPdf.new(#user, view_context, #details)
send_data pdf.render, filename: "#{#user.id}.pdf",
type: 'application/pdf',
disposition: 'inline'
end
end
end
In above action, I generate the pdf and show it in browser and it works perfectly. But I want to show the pdf in browser and also save the pdf at server in public/user_details directory. How can I do that?
There are some good gems that help in file uploading and saving. Here is a list of these gems. Paperclip and Carrierwave are the most popular options.
You could also implement it from scratch. Rails has built-in helpers which make it easy to roll your own solution.
pdf_content = *Content you want to save in the file*
File.open(Rails.root.join('public', 'user_details', filename), 'wb') do |file|
file.write(pdf_content.read)
end
It depends on the complexity of what you want to achieve, but this is totally sufficient for easy file saving tasks. Go here to find more information.
You could do this
pdf_string = render_to_string :template => 'template', :layout => false
File.open("public/userfiles/example.pdf", 'wb') { |
f| f.write(pdf_string) }
end
Render the template to string and then create a file and write that pdf to it..
This should help

carrierwave_direct exception empty file

I use carrierwave_direct to upload a file , but when the uploaded file is empty I got such error:
<Error><Code>EntityTooSmall</Code><Message>Your proposed upload is smaller than the minimum allowed size</Message><MinSizeAllowed>1</MinSizeAllowed><ProposedSize>0</ProposedSize><RequestId>FE2FE66371A06E29</RequestId><HostId>k3aJ8EtnmPvVxJszZukKYbtEO5Ddrjq/a+FdhqTyL1nZ7afy+msTAA1MiwX8lXvH</HostId></Error>
How to catch this exception??
My code:
collection_action :new_import do
#uploader = #organization.import_file
#organization.save
#uploader.success_action_redirect = import_admin_organization_rooms_url(#organization)
end
collection_action :import do
RoomImportWorker.perform_async(params[:bucket],params[:key],#organization.id,current_user.id)
redirect_to admin_organization_rooms_path(#organization), :notice => "Your request has been accepted! We will inform you via email about the results!"
end
This validation looks good i guess:
https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Validate-attachment-file-size

Ruby on rails, forcing the user to download a tmp file

I've created a file in the tmp directory with the following controller code:
def download
file_path = "#{RAILS_ROOT}/tmp/downloads/xxx.html"
data = render_to_string( :action => :show, :layout => nil )
File.open(file_path, "w"){|f| f << data }
flash[:notice] = "saved to #{file_path}"
end
This creates the file I wanted in the tmp directory, what I want to do is force the user to download that file.
On my local machine, the file is saved to path like:
/Users/xxxx/Documents/Sites/xxxx/Website/htdocs/tmp/downloads/xxxx.html
And on the live server this url will be somthing totally different.
What I was wondering is how do I force the user to download this xxxx.html ?
P.S.
If I put a...
redirect_to file_path
...on the controller it just give's me a route not found.
Cheers.
Take a look at the send_file method. It'd look something like this:
send_file Rails.root.join('tmp', 'downloads', 'xxxxx.html'), :type => 'text/html', :disposition => 'attachment'
:disposition => 'attachment' will force the browser to download the file instead of rendering it. Set it to 'inline' if you want it to load in the browser. If nginx is in front of your Rails app then you will have to modify your environment config (ie. environments/production.rb):
# For nginx:
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
It's easy to confuse file paths with URLs, but it is an important distinction. What has a URL path of /a/b.txt is actually located in the system path #{Rails.root}/public/a/b.txt so you may need to address this by generating both in tandem.
Here's how you might address that:
def download
base_path = "downloads/xxx.html"
system_path = File.expand_path("public/#{base_path}", Rails.root)
url_path = "/#{base_path}"
File.open(file_path, "w") do |f|
f.puts render_to_string(:action => :show, :layout => nil)
end
flash[:notice] = "saved to #{base_path}"
redirect_to(url_path)
end
You cannot redirect to a resource that is not exposed through your web server, and generally only things in public/ are set this way. You can include additional paths if you configure your server accordingly.
You can also side-step this whole process by simply rendering the response as a downloadable inline attachment, if you prefer:
render(:action => :show, :layout => nil, :content_type=> 'application/octet-stream')

Resources