How to view/download file under a custom subdir in Rails 4? - ruby-on-rails

After uploading a file, we would like to make the file available for view and download. The file could be in common format such as pdf, jpg or xls. After a user clicks the file name, the file is downloaded and opened by local app on user's PC. Here is what we did:
<%= link_to r.file_name, r.storage_subdir + '/' + r.file_name %>
r.storage_subdir returns storage/multi_item_orderx_order/15. storage is a subdir under root. r.file_name returns IMG_3002.jpg
When clicking the file name, there pops up an error:
No route matches [GET] "/upload/storage/multi_item_orderx_order/15/IMG_3002.jpg"
(Not sure how upload was put in front of storage). Used save link as to download and the file is only a partial in size and there shows nothing after clicking it. What is the right way to both view and download a file? Or at least to download a file.

If you don't understand how to render uploaded file then you uploading it wrong. Here is a small tutorial for you.
How to implement file upload function in your rails application
First of all add this gems to your Gemfile and then execute bundle install in console.
gem "rmagick"
gem "carrierwave"
Let's imagine that you have model User and you need to give users ability to upload profile picture. When bundler install successfuly installed new gems we need to create Uploader via generator.
rails g uploader avatar
And let's configure it:
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
def store_dir
"uploads/#{mode.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process resize_to_limit: [64, 64]
end
end
Few words about this configuration: we said carrierwave to use RMagick gem, said which storage we would like to use and where data must be stored and implemented resize for thumbnails.
Now need to add column avatar to users table. Let's create migration:
rails g migration add_avatar_to_users avatar:string
And now lets mount our uploader in User model:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
That's all! Carrierwave configured and ready to use in your application! Now you can create view like:
<%= form_for #user, html: {multipart: true} do |f| %>
<%= f.file_field :avatar %>
<%= f.submit %>
<% end %>
This form will give you ability to upload avatar. In controller you need to pass params[:user][:avatar] to model to create\update user's avatar.
And if you need to render image to view you can use something like this:
<%= image_tag #user.avatar_url(:thumb) %>
or
<%= image_tag #user.avatar.thumb.url %>
Don't forget to install imagemagick headers via system's package manager (apt-get \ yum \ pacman \ brew \ etc) because if you will not do it, bundler will fall.
If you got any questions, check out carrierwave github page and carrierwave documentation.

Related

Get path to ActiveStorage file on disk

I need to get the path to the file on disk which is using ActiveStorage. The file is stored locally.
When I was using paperclip, I used the path method on the attachment which returned the full path.
Example:
user.avatar.path
While looking at the Active Storage Docs, it looked like rails_blob_path would do the trick. After looking at what it returned though, it does not provide the path to the document. Thus, it returns this error:
No such file or directory # rb_sysopen -
Background
I need the path to the document because I am using the combine_pdf gem in order to combine multiple pdfs into a single pdf.
For the paperclip implementation, I iterated through the full_paths of the selected pdf attachments and load them into the combined pdf:
attachment_paths.each {|att_path| report << CombinePDF.load(att_path)}
Use:
ActiveStorage::Blob.service.path_for(user.avatar.key)
You can do something like this on your model:
class User < ApplicationRecord
has_one_attached :avatar
def avatar_on_disk
ActiveStorage::Blob.service.path_for(avatar.key)
end
end
I'm not sure why all the other answers use send(:url_for, key). I'm using Rails 5.2.2 and path_for is a public method, therefore, it's way better to avoid send, or simply call path_for:
class User < ApplicationRecord
has_one_attached :avatar
def avatar_path
ActiveStorage::Blob.service.path_for(avatar.key)
end
end
Worth noting that in the view you can do things like this:
<p>
<%= image_tag url_for(#user.avatar) %>
<br>
<%= link_to 'View', polymorphic_url(#user.avatar) %>
<br>
Stored at <%= #user.image_path %>
<br>
<%= link_to 'Download', rails_blob_path(#user.avatar, disposition: :attachment) %>
<br>
<%= f.file_field :avatar %>
</p>
Thanks to the help of #muistooshort in the comments, after looking at the Active Storage Code, this works:
active_storage_disk_service = ActiveStorage::Service::DiskService.new(root: Rails.root.to_s + '/storage/')
active_storage_disk_service.send(:path_for, user.avatar.blob.key)
# => returns full path to the document stored locally on disk
This solution feels a bit hacky to me. I'd love to hear of other solutions. This does work for me though.
You can download the attachment to a local dir and then process it.
Supposing you have in your model:
has_one_attached :pdf_attachment
You can define:
def process_attachment
# Download the attached file in temp dir
pdf_attachment_path = "#{Dir.tmpdir}/#{pdf_attachment.filename}"
File.open(pdf_attachment_path, 'wb') do |file|
file.write(pdf_attachment.download)
end
# process the downloaded file
# ...
end

To display default user image if image is not present (ruby on rails)

I want to write a condition in ruby to display default image if no user image is present , if present display the present image.
This is my index.html.slim page:
#product.reviews.each do |r|
a href="#"
= image_tag r.user.image_url :thumbnail`
I recommend you add the code for that in the helper or model, to keep the code clean.
helper method:
def user_avatar user
if user.image.present?
image_tag user.image_url :thumbnail
else
# Assuming you have a default.jpg in your assets folder
image_tag 'default.jpg'
end
end
view page:
#product.reviews.each do |r|
a href="#"
= user_avatar r.user
Also, from the code i feel like you are using a library like paperclip or carrierwave.
If so in paperclip you can set the default image by placing this code in the model.
:default_url => "/assets/:style/missing_avatar.jpg"
if you are using carrierwave place this method in your uploader file.
def default_url
"/images/fallback/default.png"
end
Assuming that you are using paperclip or something to save your images to the database, you can check if image exists by having
if r.user.image.present?
= image_tag r.user.image_url :thumbnail
else
= image_tag 'your default image path'
end
you should check your image gem to get find out whether this is valid as I don't know what kind of setup you have.
I managed to take care of this by using erb inside of the view used to display a user.
One nice thing about this approach is you won't need to save a default photo multiple times for multiple users. You can keep one inside of assets/images and just rely on it.
I was using Active Storage and the following gems to take care of uploading and resizing of photos:
gem 'image_processing', '1.9.3'
gem 'mini_magick', '4.9.5'
gem 'active_storage_validations', '0.8.9'
and wrote the following code inside of the necessary view:
<% if user.image.attached? %>
<%= image_tag user.image.variant(resize_to_limit: [200, 200]) %>
<% else %>
<%= image_tag("default.jpg", alt: "A stern frog", width: "200") %>
<% end %>
Just make sure you have a photo named default.jpg inside of assets/images

How do I add a version name to the front of a dynamic image asset in rails

I have a rails 4 app. I am uploading assets using Carrierwave and it allows me to save resized versions as well as the original. If I upload 'myimage.jpg' it will also save 'xs_myimage.jpg' to AWS S3. The problem is, the xs version name isn't stored anywhere, so I don't know how to serve that image.
This is my image call in the view:
<%= image_tag (#portfolio.img) %>
Some how I'd like to call the xs_myimage.jp:
<%= image_tag 'xs_'(#portfolio.img)%> #I know this doesn't work, its just what I've tried
Carrierwave will automatically generate an URL helper for every version of the asset you declare. Consider this:
class MyUploader < CarrierWave::Uploader::Base
version :xs do
# ...
end
end
You can then use the generated helper:
uploader = MyUploader.new # or get the instance out of your model (#portfolio.img)
uploader.xs.url
In Carrierwave uploader
version :xs do
process resize_to_limit: [50, 50]
end
then in view
<%= image_tag(#portfolio.img.xs.url) %>
Try to use this
<%
parts = #portfilio.img.split('/')
parts[-1]="xs_#{parts.last}"
img = parts.join('/')
%>
<%= image_tag img %>
But its very bad solution.
If "xs" is a smaller(bigger) size of image why you doesn't use carrierwave thumbs?

Rails: Uploading Images to Amazon S3 via Carrierwave only works on "Edit"

(An Image belongs to a Post)
The image uploader works, but only when I upload the image on Post Edit. I want the image to upload as the Post is created.
The Image upload ("<%= f.file_field :image %>") is within the New Post Form, so I'm guessing Carrierwave is trying to upload the image to the designated path before the Post is created, causing it to not know where to upload.
Here is the ImageUploader file:
class TrackImageUploader < CarrierWave::Uploader::Base
def cache_dir
"#{Rails.root}/tmp/uploads"
end
include CarrierWave::RMagick
#storage :file
#storage :fog
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :featured do
process :resize_to_fill => [210, 145]
end
def extension_white_list
%w(jpg jpeg gif png)
end
end
I am guessing the store_dir from the code above is in charge of saving the image in the according path on Amazon S3.
How would I make it so that it uploads the image AFTER the Post is made, so that it can get the Post.id ?
I had exactly the same problem, but I've found a solution.
My ImageUploader.rb looks like this: https://gist.github.com/egbertp/7572501. On line 9 of this file you'll see include CarrierWaveDirect::Uploader. By using the direct uploader technique, your site-user will upload his/her file directly to the Rackspace cloud. However, at the time of uploading, the Image model is not yet created in you database, causing an error (as you guessed correctly).
By disabling the direct upload technique, the site-user sends the image to your webserver fist and afterwards the Image model is created and saved in the database. In my ImageUploader class this is also necessary because I would like my webserver to process the image, in order to create a thumbnail version.
I hope this will help you, or somebody else in the future.
Best regards,
Egbert
I ve also done the same but i ve used the cloudinary gem to maintain my images. Can you please show me ur controller where u ve written the edit action.
I m showing you my code u can tally it with urs
in the controller in edit action
def create
#img = Image.new(params[:img])
if #img.save
redirect_to(:action => 'show')
else
render('c_view')
end
end
in the view from where it is taking the image
<%= form_for :img, :url => {:action => "create"} do |f| %>
<%= f.label "Upload an image"%>
<%= f.hidden_field(:image_cache)%>
<%= f.file_field :image %>
<%= f.submit "Submit", class: "btn btn-success"%>
<% end %>
To show the image in some view page ive used this
<% #img.each_with_index do |i|%>
<%= image_tag (i.image.url) %>
<%end%>

Upload Video in a rails application

I need a plugin for "Upload Videos" in a rails application.
Can anyone give me an idea on how to do it?
You can also use carrierwave gem
Add in your Gemfile: gem 'carrierwave'
Run bundle install
Create an uploader for uploading video using carrierwave generator.
rails g uploader video
It creates file video_uploader.rb in uploaders directory
Create a migration in model where you want attach the video or image, be careful with the migration name it should be like add_{column}_to_{model}
rails g migration add_video_to_post video:string
Migrate database
Run rake db:migrate
Add uploader to the model
class Post < ActiveRecord::Base
mount_uploader :video, VideoUploader
end
Add video parameter in PostController
class PostController < ApplicationController
.
.
.
def post_params
params.require(:post).permit(:name,:video)
end
end
Add file attachment field in _forml.html.erb which is in views/posts
<%=f.file_field :video%>
To view/stream the video
<% #posts.each do |post|%>
<%= post.name %>
<%= video_tag post.video_url.to_s :controls =>true %>
<%end%>
for more information
carrierwave gem
https://github.com/carrierwaveuploader/carrierwave
and video tutorial
http://railscasts.com/episodes/253-carrierwave-file-uploads
Even more specific, I created a Rails gem that works specifically with videos:
https://rubygems.org/gems/paperclip-ffmpeg
Try paperclip gem, very popular for this purpose

Resources