Rails simple file download - ruby-on-rails

hi help me in simple question:
how to released simple download:
i my public/data i have some.txt or some.pdf file ( with some text )
and i want to user click in some button and start download this file.
i do something like that
<%= link_to "Terms" ,:action => :download, :file_name => 'some.txt' %>
def download
send_file "#{RAILS_ROOT}/public/data/#{params[:file_name]}", :type=>"application/zip"
end
But what to do next?
PS if you have some tutorial or example on this subject (like downloading file), I would be very grateful

You are definitely missing a route in your routes.rb:
resources :posts do
get :download, :on => :collection
resource :comments
end
and then you can have a link like:
<%= link_to "Terms" ,:action => :download, :file_name => 'some.txt' %>
that will probably generate something like:
Terms
I think it's still not the best solution, but solves your problem.
I recommend a quick look at this part of the Rails Guides to clarify this:
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
A complete read of this guides is always good and highly recommended too.

What exactly do you mean "what next"? Apart from the fact that the application/zip mime type doesn't match your file's (which is .txt, so presumable text/plain) what you wrote is essentially correct.
Two things to keep in mind, since your file is in the public directory you don't strictly need to use send_file through the controller, you could just write this instead:
<%= link_to "Terms", '/data/some.txt' %>
And second if you're going to use send_file to server larger files you might want to look in to xsendfile instead which improves performance. This has to be supported by your webserver though (using mod_sendfile for apache for example.)
But all that said your code should work as is.

Related

Link_to efficiency without fetching record

In my Rails app there are experiment instances and each experiment has attachments, which are represented as binary blobs in the database and can be quite big. This question is about efficiency in coding a link_to show the attachment.
Originally I had this:
<%= link_to #experiment.attachment.file_name, #experiment.attachment %>
However I was told that the Rails app would be more efficient in rendering the page with
<%= link_to #experiment.attachment.file_name, {:controller => :attachments, :action => :show, :id => #experiment.attachment_id}, {:method => :get} if ! #experiment.attachment_id.nil? %>
The justification is that the first version fetches the attachment from the database, and the second one does not, making it is better, albeit longer and uglier. Is this true?
Both versions accomplish the same thing in directing the user to the show page for an attachment and I was under the impression the first is the default way to do a link_to a record show page.
Is there a way to shorthand the second piece of code to make it less terrible with code in the view?
Try using a rails route helper, use rake routes to view all your routes and then you can get something like this (don't forget to apped _path to route):
experiment_attachment_path(#experiment.attachment)
I would express this using the route path rather than using the ActiveRecord instance to load so the view can use its to_param method (which is what it is doing under the hood).
<%= link_to #experiment.attachment.file_name, attachment_path(#experiment.attachment_id) %>

Paperclip Force Download

Hope anyone can help on this!
Making use of Paperclip to upload Files to an Application, so two things that i need to cover.
1) What is the best way to link to the File for Download, I am currently linking to the public folder but it is not the best option as it shows the URL which i dont want. Thinking maybe a button_to call be not sure if this is build into Paperclip already. Options.
2) When above is done then the Browser needs to force to download not just open the file, or at least give the user the standard firefox option open or save.
Please Assist Thanks a Mil!!!
I'll answer question NÂș2.
Let's say your model is called gallery.
In your gallery controller you'll add a download method:
def download
#gallery= Gallery.find(params[:gallery_id])
send_file #gallery.gallery_picture.path,
:filename => #gallery.gallery_picture_file_name,
:type => #gallery.gallery_picture_content_type,
:disposition => 'attachment'
end
Now from your routes you'll invoke the root to this method:
match 'gallery/:id' => 'gallery#download', :as => :download
In your views:
- #galleries.each do |gallery|
= link_to 'Download Picture', download_path(gallery.id)
I'm away from home and I can not test this code, but you can visit those questions I did with the same question as yours. Let me know if it solves your problem.
Rails send_file multiple styles from Paperclip, how can I avoid code repetition?
Rails 3.0 associated model with download 'save to' image
In rails, send_file with a parameter set by user is a security hole. More information
In rails 4 you can't just write match, you have to add at the end , via: [:get, :post]. More information

link_to a form in ruby

I want to use the link to function to point to a form.
i.e <%= link_to 'Reports', '/reports/index.html.erb' %>
But this gives me a error saying no route matches '/reports/index.html.erb.
Thanks,
Ramya.
Rails doesn't like having the document format in the URL unless it's necessary (like when one action can handle multiple request formats). If you have reports/index.html.erb, the route to it would look like one of these:
match 'reports' => 'reports#index' #=> 'yourdomain.com/reports'
match 'reports/index' => 'reports#index' #=> 'yourdomain.com/reports/index
Then your link would be:
<%= link_to 'Reports', 'reports' %>
or
<%= link_to 'Reports', 'reports/index' %>
If you really wanted to have the .html, you could probably do it like this:
match 'reports/index.html' => 'reports#index' #=> 'yourdomain.com/reports/index.html
or
match 'reports/index.:format' => 'reports#index' #=> 'yourdomain.com/reports/index.html
But the .html is meaningless in the first case and unnecessary in the second. I don't recommend doing it this way, as it's not standard Rails practice.
I highly recommend you read this tutorial on Routing, at least the first few sections, before you move forward. It's an absolutely essential part of Rails, and if you don't understand how it works, you will never be a productive Rails programmer.
The link should point to the form from the server's point of view. Try <%= link_to 'Reports', '/reports/index.html' %> (without the '.erb')
Make sure that your routes really define that url. I guess it may be '/reports/' instead of '/reports/index.html', but YMMV.
Consult the output of command rake routes to see what routes are defined.

rails how to render a file with correct filename

This is tough one to explain so i'll try my best, and hopefully edit the question if people need more information. I am not providing exact code, but merely an example of the issue.
I am using rails 2.3.8. I am on Unix.
I have a bunch of files under a directory not Apache accessible. (i.e. /data/files/file.rpk)
I have the following in my view.
link_to "RPK File", :controller => 'mycontroller', :action=> 'myaction', :file => '/data/files/file.rpk'
I have the following in my controller.
def myaction
if FileTest.exists?(params[:file])
render :file => params[:file]
end
end
When i select the link on the page i get a download prompt for my desired file, but the name of the file is "myaction" instead of the filename.
Thoughts on how i could get it named correctly?
Sounds like a job for send_file. The x_sendfile option prevents that your workers keep busy while transferring the actual file. You can read more about that in this blogpost.
send_file path_to_file_on_filesystem, :type => "application/zip", :x_sendfile => true
You want to use send_data with the :filename option. See the API documentation.
You want to be extremely careful with this, though. Never ever trust the client/user! They will send file=../../../../etc/group or something in order to read arbitrary files on your system, so be very sure to sanitize that value before passing it to any file-reading methods.

Understanding Routes in Rails

I actually have two questions. I've read the Rails guide and a couple of other articles, but I haven't been able to translate what I read into working routes. I have an application that allows the uploading of images from several different contexts. I'd like the URI to express the proper context so that the following URIs access the same page:
/images/upload
/photos/upload
In this example, I've overridden the new_image_path to use upload for descriptive purposes. I have the override working, but using :as to map images to photos only appears to work one way (with :as => 'photos' in place, the /images routes don't work). Is there a way to make multiple routes point to the same place?
I also have a couple of different ways to upload images/photos/etc. The standard method with a single image per form or a batch method where the user uploads a zip file and that archive is extracted and each of its images is saved.
It seems like the most semantic way to do this is by adding a handler component to the URI (e.g. /images/upload/batch), but I'm not sure how to handle this. The default route path seems pretty general for something that would only be required for images, but I also don't want to be so specific with a named route for the entire bit. What's the best way to do something like this?
Thanks.
Update: Based on jonnii's answer to my first question, I've added the following to my routes.rb file:
map.resources :images, :path_names => { :new => 'upload' }
map.resources :photos, :controller => 'Images', :path_names => { :new => 'upload' }
That seems to do the trick for allowing me to use /images/ and /photos/ interchangeably.
I'm assuming you're doing your photos routes using resources:
map.resources :photos
If that's the case you should be able to define a second resource pointing to the same controller as the photos resource:
map.resources :uploads, :controller => 'PhotosController'
I haven't tested this, but I don't see why something like this wouldn't work..
Question 2:
There are a few different ways you can do batch uploads, I think the best way is to have a separate resource as you're most likely going to have a different UI for it. For example you might do:
map.resources :batch_uploads
This would probably be enough if you were going to take batch uploads as a zip.
An option that's slightly more complicated but takes advantage of the rails niceties (and lets be honest, who doesn't want to take advantage of that??) is something with nested child forms and accepts_nested_attributes_for. This would be useful if you wanted to allow a user to attach more than one image to a form at time.
For example, if your model was something like:
class User < AR:B
has_many :photos
end
You could have a route like:
map.resources :users do |u|
u.resources :photos, :collection => {:get => :new_batch, :post => create_batch}
end
In your new_batch view you would have a form_for #user with a user_form.fields_for :photos. You can add a new form using ajax, whatever and post it all at once.
If you wanted to keep the same semantics as you have now and didn't want to add any more routes you could extend your model to do something different based on the filename of what is being uploaded.
For example if you were using paperclip for attachments you could stop processing the attachment if the filename ends with .zip (this code is not guaranteed to work, I'm doing it from memory):
def is_zip?
attachment.filename.ends_with?('.zip')
end
before_attachment_process do |attachment|
false if is_zip?
end
before_filter :process_bulk_attachment, :if => :is_zip?
def process_bulk_attachment
... extract the zip and save each image in it ...
false
end
The beauty of this is that it's part of the model. You should always aim for fat models and skinny controllers!
I hope this gives you a few ideas and/or points you in the right direction.
I've gotten a little closer to what I'm going for:
map.resources :images, :path_names => { :new => 'upload' }
map.resources :images, :new => { :batch => :get }
The former allows me to use /images/upload instead of /images/new, as shown in jonnii's answer to my first question. The latter allows me to specify a second route to "new" functionality via /images/new/batch which calls ImagesController#batch. I was hoping to be able to use /images/upload/batch, but this may have to do.
Clearly, I still have a long way to go before I really understand routing in Rails. jonnii, if I'm just rehashing part of what you've already said, I apologize. I may have to plead ignorance with respect to much of your answer to question 2.

Resources