Download File issue, CarrierWave upload - ruby-on-rails

Thus far I have successfully set up CarrierWave so that it can upload many files including .pdf, .mp3, .wav, .mp4, and .mov. Awesome that's a pretty good success for me.
What I am trying to do now is to display those Uploads. On that display, I would like two links, one to download the uploaded file and the other to view the uploaded file. Currently, viewing the uploaded files works when I was attempting to create a way for the user to download the file.
Currently, here is the code that allows a user to view/listen/watch the uploaded file:
<% #user.uploads.each do |upload| %>
<tr>
<td><%= link_to upload.name, upload.attachment_url.to_s %></td>
<td class="right_align"><%= upload.to_a_date %></td>
<td class="right_align"><%#= upload.file_size %></td>
</tr>
<% end %>
Clicking on the link above allows a user to view the upload. How would I allow a user to download the same content? This gives me the correct path, but when I click on the link it views the file rather than downloads it.
I have also tried to do this in html5 with the following code:
<a href="#{upload.attachment_url.to_s}" download>Download</a>
This doesn't download the correct file.

Create separate action for this and use Rails send_file method:
def download_file
upload = Upload.find(params[:id])
send_file upload.attachment.url
end

Related

Render a Markdown file stored in S3 uploaded with shrine - Rails

I'm using shrine to upload files from my rails application to S3. All is working fine, but I dont know how to display that file using redcarpet gem.
For example I can do this:
<div>
<%= markdown("##title
* ") %>
</div>
And works fine.
But if I do this:
<%= markdown(#rfile.rfile.url) %>
Is showing me a download link from S3.
How I can get the file content and not the file link?
Call to #rfile.rfile returns a Shrine::UploadedFile object, which has many more convenient methods other than just #url. On such method is #read, which retrieves content of the file:
<%= markdown(#rfile.rfile.read) %>
However, in this case the file would be opened and read, but not closed. So it's better to call #open with a block, and call #read on the yielded IO object, which can be neatly written as
<%= markdown(#rfile.rfile.open(&:read)) %>

Ruby On Rails file selector

I am developing my first Ruby On Rails project and I have the following question.
Is it possible to display a window with the contents of a folder so I can select a file? For example, images from the "app/assets/images" project folder.
I have searched in Google and rubygems.org but have not found anything.
Respectfully,
Jorge Maldonado
You could write a controller method and associated view that scan over a folder and output the contents. Here's an example.
Controller:
def folder
#files = Dir.open(Rails.root.join('public', 'images'))
end
View:
<table>
<% #files.each do |file| %>
<tr><td><%= link_to file, file_path(file) %></td></tr>
<% end %>
</table>
Note that file_path is assumed to be some route defined in routes.rb which accepts a file name and allows the user to view/open/download that file.
Here's a gem that also gives similar functionality:
https://github.com/furkancelik/fcfinder

Downloading S3 files(objects) using aws-sdk for ruby in rails

I am not using paperclip or carrierwave or any other gems for interaction with amazon web services s3. In fact, I am not using any models, just directly interacting with S3 objects.
Can someone please provide some code as to how to directly download objects(files) from AWS S3
This is my code so far :
View:
<h2>Download Files</h2>
<%#root_files.each do |file| %>
<% next if #obj %>
<td>Filename: <b><%= file %></b></td>
<td><%= link_to "Download", download_url_for(file) %></td>
<% end %>
Corresponding Controller:
def xxx
bucket = AWS::S3.new.buckets[ ENV["BUCKET"]]
#root_files = bucket.as_tree.children.select(&:leaf?).collect(&:key)
end
download_url_for method:
def download_url_for(file_key)
s3 = AWS::S3.new
bucket = s3.buckets[ ENV["BUCKET"]]
object = bucket.objects[file_key]
File.open('xxxxx', 'wb') do |file|
object.read do |chunk|
file.write(chunk)
end
end
end
I need to be able to save the downloaded file on any browsable non-pretedetermined location on my desktop.Id really appreciate view on how to modify the download_url_for method to achieve this.
=====================================
I just tried using
<td><%= link_to "Download",object.value.url_for(:read).to_s %></td>
But on downloading I only get a zip file with xml content inside it, not the original file (word document in my case) --- any thoughts on how to modify this so as to get the actual file type and contents? Thanks
=======================Heres my code for uploading the file
View code:
<%= form_tag({:action => 'create_file'}, multipart: true) do %>
<%= file_field_tag 'uploaded_file' %><br />
<%= submit_tag 'Upload' %> <br />
<% end %>
Controller action - create_file action
def create_file
s3 = AWS::S3.new
bucket = s3.buckets[ENV["BUCKET"]]
key = params[:uploaded_file].original_filename
object = bucket.objects[key]
object.write(params[:uploaded_file].read)
end
Can someone suggest as to how I can check the MIME type or preserve the filename while uploading if thats the issue as mentioned by #Frederick Cheung? thanks
You'd probably be better off providing a url for users to download directly from S3 rather than trying to stream the file through your Rails app.
The aws-sdk provides the url_for method to accomplish this: http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html#url_for-instance_method. It takes a bunch of options for expiration, security, caching, etc. A basic example:
<td><%= link_to "Download", file.url_for(:read) %></td>
--EDIT--
To access the url_for method in the view, you'll need references to the s3 objects. So in your controller, you want to collect objects from the leaf nodes, not keys:
#root_files = bucket.as_tree.children.select(&:leaf?).collect(&:object)

How to properly handle "image not found" errors in Rails using Paperclip

I am using Paperclip to store profile pictures for users. I am trying to be quite strict, by allowing only certain file types, etc. Nevertheless it is quite common for users to manage to mishandle something.
In my index.html.erb file I list all user profiles, but it is common for this list to throw Application Error on the production environment, if one of the images is damaged, cannot be found etc, the whole thing fails, and the whole page fails.
Currently I find the image like this
<% #members.each do |member| %>
<tr>
<td><%= image_tag member.avatar.url(:thumb) %></td>
</tr>
<% end %>
How do I wrap this in a code, that would not allow the page to fail, if for some reason the image fails, and would display an alternative image, as specified in PaperClip documentation (aka default image).
I would suggest to write a method in model that checks the condition and call the method from View.
Model:
def image_url(image_size)
avatar_image = member.avatar
if avatar_image.present? && avatar_image.url(image_size).present?
return avatar_image.url(image_size)
else
return "/images/default_image.jpg"
end
end
View:
<%= image_tag member.image_url(:thumb) %>
Try this.
In your model:
has_attached_file :image, :default_url => "/images/no-image.jpg"

Carrierwave Temporary File Without Model

I need to be able to attach a file to a mail (using Mailer) for a recently uploaded file which is not linked to any Model.
In the code that goes for the upload form:
<%= form_for(:mail, :url => {:action => 'send_mail'}, :html => {:multipart => true}) do |f| %>
<table summary="send_table">
<tr>
<th>Attachment</th>
<td><%= f.file_field(:attachment) %><a id="attachment"></a></td>
</tr>
</table>
<%= submit_tag "Send!" %>
Now, what I am looking into doing in the send_mail action is something like:
MyMailer.send_mail(params[:mail][:attachment]).deliver
with params[:mail][:attachment] being the path to the temp file uploaded with the form. How can one do that?
This also implies another question: Should I manually delete the file from the temp once the mail is sent? If yes, how?
Copying the answer from the comments in order to remove this question from the "Unanswered" filter:
Finally nailed it:
unless (params[:mail][:attachment]).nil?
uploader = AttachmentUploader.new
uploader.cache!(params[:mail][:attachment])
#file_name = uploader.filename
#file_data = uploader.file.read()
end
and then
MyMailer.send_mail(#file_name,#file_data)
~ answer per user1563325
I'd like to add that for the scenario described here, maybe you don't need CarrierWave for such a temporary upload situation. When uploading using file_field, Rails saves it into a Tempfile, whose path you can access like this:
params[:mail][:attachment].path
These files are deleted automatically so you shouldn't need to worry about them, as the Rails docs explain:
Uploaded files are temporary files whose lifespan is one request. When the object is finalized Ruby unlinks the file, so there is no need to clean them with a separate maintenance task.

Resources