Allowing User to Download Image from my S3 Bucket - ruby-on-rails

I am trying to construct a method which will allow a user to download a pdf file from my s3 bucket, (which at the moment it does)
But some parts of the url may change from time to time, like the id number after /timetables, the filename (but will always be a pdf) and the id number after the filename
This is what i have so far
def download
path = "images/timetables/14/original.pdf?"
data = open("http://#{ENV['AWS_BUCKET']}.s3.amazonaws.com/#{path}/1392904333")
send_data data.read, filename: 'Timetable',
type: 'application/pdf',
disposition: 'attachment',
stream: 'true',
buffer_size: '4096'
end
is there a way to get this information on the fly or can i only hardcode it? Ideally i want to store as much as the path into variables as i can.
Could anyone show me how to do this correctly please.
Thanks

It'll be easier to use paperclip to manage the uploading. Using this you'll create a table to save a record which will have a dynamic reference to your uploaded file on S3.
The workflow goes like this:
You upload the PDF in question to your app
paperclip uploads it to S3 and saves the URL to your database
you provide a link to your users for the S3 URL of the PDF
OR you give them the link to your record and you initiate the download as you posted above
First the model which will hold the pdf file reference looks like this:
class PdfRecord < ActiveRecord::Base
has_attached_file :pdf, :storage => :s3,
:bucket => 'S3_BUCKET_NAME',
:s3_credentials => {
:access_key_id => 'AWS_ACCESS_KEY_ID',
:secret_access_key => 'AWS_SECRET_ACCESS_KEY'
}
end
Create a form for yourself to upload your pdf:
# app/views/pdf_records/new
<%= form_for #pdf_record, multipart: true do |f| %>
<%= f.label :pdf %>
<%= f.file_field :pdf %>
<%= f.submit %>
<% end %>
Using this form you can upload the pdf and paperclip will save it to S3 and keep a reference to it in your database.
Give your users a link to your pdf_record like this:
#pdf_record = PdfRecord.find(which ever one you want)
#pdf_record.pdf.url # => the S3 url
Giving your users that url will let them download the pdf directly however, if you want to hide the URL and also allow you to update the pdf without breaking the link you can give the users a link to the pdf_record and then you initiate the download from your controller:
# give your users this url, as defined in your routes.rb
pdf_record_url(#pdf_record)
Then in the show action of the pdf_record controller:
def show
pdf_record = PdfRecord.find(params[:id])
data = open(pdf_record.pdf.url)
send_data data.read, filename: pdf_record.pdf_file_name,
type: pdf_record.pdf_content_type,
disposition: 'attachment',
stream: 'true',
buffer_size: '4096'
end
Now, you can update that pdf_record whenever you want and since your users have a link to the record rather than the actual pdf file the link will always work.

Related

Setting URL/PATH correctly for AWS S3 by using Paperclip/Rails

I have two models: Collection and Letter. Collection has many Letters and Letter obviously belongs_to Collection.
Below is my Letter.rb file:
class Letter < ActiveRecord::Base
belongs_to :collection
has_attached_file :pdf,
:url => "/pdf/:id/:style/:basename.:extension",
:path => "/pdf/:id/:style/:basename.:extension",
:s3_host_name => host_name,
:storage => :s3,
:bucket => ENV['S3_BUCKET_NAME'],
:s3_credentials => {
:access_key_id =>#ENV['AWS_ACCESS_KEY_ID'],
:secret_access_key =>ENV['AWS_SECRET_ACCESS_KEY']
}
validates_attachment :pdf,
:content_type => {
:content_type =>
["application/pdf", "text/plain", /\Aimage\/.*\Z/, "application/msword"]
}
end
I have a question about url and path attribute in has_attached_file.
Instead of setting letter model's id in path, I would like to set collection's id. Additionally, I also wanna put title which is Letter's attribute. Let's say #collection's id is 1. #collection has #letter1 and #letter2. When I save the files to AWS S3, I want to save it under /pdf/1(which is collection_id)/:title. How can I write this in url and path?
In Paperclip, you can use interpolations for that.
Your has_attached_file method would look somewhat like this;
has_attached_file :image, :default_url => "/pdf/:collection_id/:title/:basename.:extension"
Create an interpolation file, called paperclip.rb or interpolations.rb in the config/initializers directory (rails picks up any script in that folder on startup), which contains code that looks somewhat like this;
Paperclip.interpolates :collection_id do |attachment, style|
attachment.instance.collection_id
end
Add the :title interpolation in the same way; add it to your has_attached_file urls, and create a second interpolation for that.
You can read more about this at https://github.com/thoughtbot/paperclip/wiki/Interpolations
In your case, I would suggest to also include the :id of the Letter in the URL, as it might be possible the user uploads two documents with the same title which might conflict.
has_attached_file :image, :default_url => "/pdf/:collection_id/:id/:title"
Paperclip uses the interpolations :basename, :extension and :style by default to create a unique path for the file.
:basename is the base file name of the uploaded file
:extension is the extension of that uploaded file
:style is the "style" or size of that uploaded file
You can specify multiple styles (like thumnails in various versions). The default style is "original", which will contain the original uploaded file.
Read more about styles here; https://github.com/thoughtbot/paperclip/wiki/Thumbnail-Generation
Always try to keep the original file as it can be handy in the future; when your site/application layout changes and new thumbnail sizes are required. You can rebuild/regenerate your whole thumbnail library from the original version.
Read more about generating/regenerating thumbnails here; https://github.com/thoughtbot/paperclip/wiki/Thumbnail-Generation#generatingregenerating-your-thumbnails
Using gem aws-sdk for a ror application for uploading images to s3
Uploading images to a fixed bucket with different folders for each object or application.
The s3 keeps a limitation on the number of buckets creattion whereas there is no
limitation for content inside a bucket.
This code will upload image for a user to s3 using aws-sdk gem. The bucket and the image uploaded are made public
so that the images uploaded are directly accessible. The input it takes is the image complete path
where it is present, folder in which it should be uploaded and user_id for whom it should
be uploaded.
def save_screenshot_to_s3(image_location, folder_name,user_id)
service = AWS::S3.new(:access_key_id => ACCESS_KEY_ID,
:secret_access_key => SECRET_ACCESS_KEY)
bucket_name = "app-images"
if(service.buckets.include?(bucket_name))
bucket = service.buckets[bucket_name]
else
bucket = service.buckets.create(bucket_name)
end
bucket.acl = :public_read
key = folder_name.to_s + "/" + File.basename(image_location)
s3_file = service.buckets[bucket_name].objects[key].write(:file => image_location)
s3_file.acl = :public_read
user = User.where(id: user_id).first
user.image = s3_file.public_url.to_s
user.save
end
Use key = folder_name.to_s + "/" + File.basename(image_location) to customize the path you want to have.

Use PDF as an image Rails

I'm using paperclip to upload my images, in this instance i would like to use the pdf as an image and enable the user to have the option of downloading it. I upload my images to an S3 bucket.
Currently when trying to render the image it fails to load the given URL
<% for i in #timetable %>
<%= image_tag(i.photo.url(:timetable)) %>
<% end %>
Whereas the exact same code works if the image type is png or jpeg for example
The HTML generated is
<img src="http://ymcagym.s3.amazonaws.com/images/timetables/13/timetable.pdf?1392893849" alt="Timetable">
When putting the link in the url it renders but opens in a pdf reader
How would i go about getting it to open as an image? and also having the option to download
The
Thanks
You can enforce the file format when specifying your styles, for example:
has_attached_file :photo,
:styles => {
:timetable => ['100x100#', :jpg],
...
}
this should create a preview image of the first page for you on upload. Be sure to run rake paperclip:refresh to regenerate your assets.

When using redirect_to files on s3. How to either display an image or download a file

In Rails 3 AttachmentsController, I have the following:
def show
attachment = Attachment.find_by_id(params[:id])
redirect_to(attachment.authenticated_url())
end
Where authenticated_url is simply a URL to S3 to access the file.
The problem is that the file is always downloaded by the browser. What I would like to have happen is if the file is an image/pdf, something the browser can render, show the file in the browser and only download non-browser friendly files.
Have you seen this before? Any ideas on where to start?
Thanks
send_file can be used for remote url as well
file = open("http://cdn2.example.com/somefile.pdf")
send_file(file, :filename => "example.pdf", :type => "application/pdf" , :disposition => "attachment")
Here example.pdf will be downloaded. If you want open pdf in browser itself use this
file = open("http://cdn2.example.com/somefile.pdf")
send_file(file, :filename => "example.pdf", :type => "application/pdf" , :disposition => "inline")
redirect_to #attachment.url
I'm using Paperclip as well, and this display pictures inside the browser. Do you have to do something different than this?
I think you'll want to look into the rails send_file method.
1) http://apidock.com/rails/ActionController/DataStreaming/send_file
The :disposition option lets you decide whether a file will be downloaded or displayed inline. I used this in my rails app to let users download mp3 files.
Hope this helps.

Showing images with carrierwave in rails 3.1 in a private store folder

I have a rails 3.1 app and I am adding carrierwave to store images. But I want to store those images outside the public folde,r because they should only be accessible when users are loaded in the app. So I changed the store_dir in carrerwave with the initializer file:
CarrierWave.configure do |config|
config.root = Rails.root
end
And my carrierwave uploader goes like this:
class ImageUploader < CarrierWave::Uploader::Base
...
def store_dir
"imagenes_expedientes/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
Images are stored correctly and if I use the public folder everything works fine. However when trying to move things to the private folder, images are not displayed and when I try to open them in a new window I get the following error:
Routing Error
No route matches [GET] "/imagenes_expedientes/volunteer/avatar/15/avatar.jpg"
I was trying to deliver the files using send_file through the controller, but instead of loading the page I only get the image downloaded.
def show
send_file "#{Rails.root}/imagenes_expedientes/avatar.jpg", :type=>"application/jpg", :x_sendfile=>true
end
Finally Images are displayed like this in the views:
<%= image_tag(#volunteer.avatar_url, :alt => "Avatar", :class => "avatar round") if #volunteer.avatar? %>
This may probably be solved rather easy, but since I am somehow new to Rails, I donĀ“t know what to do it. Should I set a route? Or is there anyway to display the images using the send_file method?
Thanks!
ANSWER
I managed to display images using x-sendfile and putting :disposition => 'inline' as suggested by clyfe. I made a new action in my controller:
def image
#volunteer = Volunteer.find(params[:id])
send_file "#{Rails.root}/imagenes_expedientes/#{#volunteer.avatar_url}",:disposition => 'inline', :type=>"application/jpg", :x_sendfile=>true
end
Added to the routes:
resources :volunteers do
member do
get 'image'
end
end
And displayed in the views:
<%= image_tag(image_volunteer_path(#volunteer), :alt => "Avatar", :class => "avatar round") if #volunteer.avatar? %>
Hope it helps others!
:disposition => 'inline' will make your images display in the browser instead of popping up the download dialog.
def show
send_file "#{Rails.root}/imagenes_expedientes/avatar.jpg", :disposition => 'inline', :type=>"application/jpg", :x_sendfile=>true
end
Depending on the images sizes and the number of users you might want to move this action to a metal app, or a separate so as to not block the server.

Download Image attached using Paperclip

Is there a way to make the users download the images attached using paperclip?
I just made a link like this:
link_to 'imagename', image.attachment.url
But this makes the browser open the image. I want the browser to ask the user to save the image. Is there a way to do that?
When it comes to sending a file you can find all information in here http://api.rubyonrails.org/classes/ActionController/Streaming.html There are most important cuts:
Simple download:
send_file '/path/to.zip'
Show a JPEG in the browser:
send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
Show a 404 page in the browser:
send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
to use one of this option you have to create a new action in controller like this:
class SomeController < ApplicationController
def download_file
send_file image.attachment.path
end
end
Here's a simple solution using the HTML5 download attribute
<%= link_to image.name, image.attachment.url, download: image.attachment.original_filename %>

Resources