I have a Rails 4 app with Ruby 2.2.0.
I am building an app for where I need to store quite a bit of images. Currently the app exists and is managing their images on a local server, however we want to change that.
The app is currently deployed on Heroku and we want to use the Cloudinary service to upload(using carrierwave) new images as well as to store the already existing ones.
The issue comes from the fact that I can't seem to be able to adopt the current folder structure that the platform is using. To start with I uploaded all my files via the Media manager in cloudinary dashboard. I created two folders header and logo. In this case I will refer to the header folder as example.
class BannerUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
def store_dir
"uploads/header/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
The model:
class CompanyImage < ActiveRecord::Base
mount_uploader :file_name, BannerUploader
belongs_to :company
end
And last but not least, here is my view:
<%
work_advantages = company.presentation.work_advantages
banner = company.company_images.where(header: true).first
%>
<%= link_to company do %>
<div class="card company-presentation-card card-hover">
<div class="card-container">
<div class="row">
<div class="col-md-12">
<div class="cover-image">
</div>
<h3><%= company.name %></h3>
<% company.company_sectors.each do |s| %>
<span class="h4-alt1 company-sector"><%= s.sector.name %></span>
<% end %>
<p><%= truncate(company.presentation.work_advantages, :length => 250) %> <span class="text-muted">(lees meer)</span></p>
</div>
</div>
</div>
</div>
<% end %>
<script>
//Set dynamic background image for the cover-image <div>
$(document).ready(function(){
$('.company-presentation-card').find('.cover-image').css('background-image', 'url("<%= banner.file_name %>")')
});
</script>
Currently, the url that is being delivered in the following: http://res.cloudinary.com/hxwmb9swy/image/upload/5a444a7c191e8c98999f6d0c2d3afaf9_Arendsen_Machinefabriek.jpg however I need to have the header(the folder I created in Cloudinary) folder in there so the url should actually look like this: http://res.cloudinary.com/hxwmb9swy/image/upload/header/5a444a7c191e8c98999f6d0c2d3afaf9_Arendsen_Machinefabriek.jpg
Of course in the case of the logo, its exactly the same however instead of header it should say logo. This will also apply to other models. So my question is how can I manage this in a way, so that I can specify this folder per uploader? Any ideas are welcome.
The header or logo should be included in the image's public ID. So if you do a server-side upload, you can override the public_id method in your uploader to include the folder. For example, the following sets the public ID as the header folder with the original image's filename (without the extension, as it should):
def public_id
basename = File.basename(original_filename, File.extname(original_filename))
"header/#{basename}"
end
If you do a client-side upload, with Cloudinary's cl_image_upload for example, you can do something like:
<%= f.cl_image_upload(:image, :folder => "header") %>
For the 2nd case, there's no need to change anything in your Uploader.
Create custom name folder Cloudinary with CarrierWave
include Cloudinary::CarrierWave
def public_id
return "my_folder/" + Cloudinary::Utils.random_public_id;
end
Related
Messing with active storage for the first time. My has_one_attached setups work fine, however my has_many_attached is giving me issues both rendering the file and an incorrect download link:
# frozen_string_literal: true
class DocumentCategory < ApplicationRecord
validates :name, presence: true
has_many_attached :documents
end
seeds file:
DocumentCategory.create!(name: "Governing Documents")
file = Rails.root.join("spec/fixtures/test.png")
DocumentCategory.first.documents.attach(io: File.open(file), filename: "Random File")
view:
<% #document_category.documents.each do |file| %>
<li class='py-2 text-[14px] text-[#3182E2]'>
<div>
<%= link_to file.filename, rails_blob_path(file, disposition: 'attachment') %>
<%= image_tag file, class: 'w-full h-48 mb-1 object-cover'%>
</div>
</li>
<% end %>
For each document in the category, the image is missing, and if I click the link, it opens a new tab to a file that doesn't exist. I'm doing the same thing for each file in the loop to my singular has_one_attached on my other models.
EDIT
This turned out to be due to an existing issue with calling attach multiple times within a transaction, and I had my seeds wrapped in an active record transaction block. Removing the transaction fixed the issue.
https://github.com/rails/rails/issues/41661
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
I'm using Rails 4 and after added paper clip initializer code to initialize the user files name by user ID to give every file different name using this code
In the initialize file
# initializers images names by the model id (:id)
Paperclip.interpolates :parent_id do |a, s|
a.instance.contract.id
end
In the model
has_attached_file :photo ,
:url => "/system/files/users/:basename_:id.:extension",
:path => ":rails_root/public/system/files/users/:basename_:id.:extension"
Now it's work well by give the file his basename_fileID.file extension
but I notes that I was missed all the old files links because the new link will search about the file contain the basename and fileID something like that "image_1.jpeg" but the old link search about basename only something like "image.jpeg"
so how I can solve it in Rails without change my old files name on server ??
Is there's some thing in Rails 4 allow me to inform the links to get the old path in some cases or not ??
Finally I solved this issue by check if the file exist on server or not by using File.exist?
The code
<% if File.exist?(#user.photo.path) %>
<!--The new path -->
<%= link_to "Photo", #user.photo.url %>
<% else %>
<!--The old path -->
<%= link_to root_url+'system/files/users/'+#user.photo_file_name, :title => "show image" do %>Photo<% end %>
<% end %>
I'm working on Rails 4 project that I need to store some images uploadaded directly by the users, and I must be capable of create a images gallery where a single user can only view their own uploads.
I'm uploading files with the CArrierWave gem, and it's doing fine. I'm changed the default uploads directory ( the public folder ) to a custom folder of mine ( /uploads/.....), but I think I'll not be able to show the images cause the 'uploads' folder can't be accessed directly, but I dont want to let the images on the public folder cause I don't want them to be freely accessed by anyone.
Code of my view
<div>
<%= #photos.each do |p| %>
<div>
<%= image_tag (p.path_url) %>
</div>
<% end %>
</div>
My CarrierWave uploader class:
class PhotoupUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"../app/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.album_id}"
end
and Photo model:
class Photo < ActiveRecord::Base
belongs_to :album
attr_accessible :legend, :path
mount_uploader :path, PhotoupUploader
end
How can I proceed?
Thanks
You would need to serve your images straight from the controller (see this question on how: Rendering an image):
def image
render :text => open(File.join('/uploads', user_id, params[:image_id], "rb").read
end
Usage:
<% image_tag "image?image_id=154" %>
(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%>