I´m using Carrierwave (2.0.2, 0.11.2) in Rails 5.2.3. and I´m pretty stuck with versions of uploaded images.
Imagemagick is latest version,gems Carrierwave,Rmagick and Minimagick installed.
Everything works as it should with two versions created by default and uncommented in uploaders/image_uploader.rb file:
just like this:
version :large do
process resize_to_fill: [500,500]
end
version :thumb from :large do
process resize_to_fit: [50, 50]
end
version :long do
process resize_to_fill: [500,200]
end
My model looks like this:
class Slide < ApplicationRecord
mount_uploader :image, ImageUploader
before_save :update_image_attributes
after_save :recreate_delayed_versions!
validates :image_title, presence: true
validates :display, presence: true
validates :order, presence: true
validates_presence_of :image
validates_integrity_of :image
validates_processing_of :image
def recreate_delayed_versions!
image.recreate_versions!(:thumb,:large,:long)
end
private
def update_image_attributes
if image.present? && image_changed?
self.image_content_type = image.file.content_type
self.image_file_size = image.file.size
end
end
end
Uploaded images are processed,scaled and saved in :original size,:large and :thumb version in correct folders...all good.
But if I try to create another version,or rename existing one,for example " version :long ",it doesn´t create it and in my views exception is caught as: "Version long doesn't exist!"
I did tried several different version names,amount of versions,name as symbol,in quotation marks,also reading the source code of Carrierwave/versions.rb,reading documentation,but still unable to find any reason for this behaviour.Only those two versions working well,not any other.Could You someone help me with it?
Thanks!
After rename the version or adding new version, you are required to recreate those versions by calling something like this:
User.find_each do |user|
user.avatar.recreate_versions!
end
Full documentation can be found here
Related
Currently with Carrierwave, after uploading a file like foo.png when creating different versions like so:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :fog
def store_dir
"#{model.class.to_s.underscore}/#{model.id}"
end
version :thumb do
process :resize_to_fit => [500, 500]
end
end
that results in the files being uploaded as:
thumb_foo.png
foo.png
I want to move "thumb" to the end of the filename for SEO reasons. Based on their docs here I added:
def full_filename(for_file)
if parent_name = super(for_file)
extension = File.extname(parent_name)
base_name = parent_name.chomp(extension)
[base_name, version_name].compact.join("_") + extension
end
end
def full_original_filename
parent_name = super
extension = File.extname(parent_name)
base_name = parent_name.chomp(extension)
[base_name, version_name].compact.join("_") + extension
end
The docs say this should result in:
foo_thumb.png
foo.png
However, I end up actually getting the following:
thumb_foo_thumb.png
foo.png
Any idea what I'm doing wrong?
Simply use #full_filename under the version block:
class AvatarUploaer < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
version :thumb do
process resize_to_fill: [50, 50]
def full_filename(for_file = model.logo.file)
parts = for_file.split('.')
extension = parts[-1]
name = parts[0...-1].join('.')
"#{name}_#{version_name}.#{extension}"
end
end
end
The result will be following:
/Users/user/app/uploads/1x1.gif
/Users/user/app/uploads/1x1_thumb.gif
More information in Wiki: How to: Migrate from one model to another
MVP example: https://gist.github.com/itsNikolay/2394f84f31db33d4c3dc6634068b0259
If you have a lot of versions, the accepted answer can get a little tedious.
I ended up overriding full_filename for everything instead of in each individual version definition. It works fine. This is for Carrierwave 1.0
photo_uploader.rb
# Override the filename of the uploaded files:
def full_filename(name)
"#{File.basename(name, '.*')}_#{version_name || 'original'}#{File.extname(name)}"
end
I'm using the built in File.basename and File.extname methods instead of doing it manually as seen in the accepted answer (although that's where I started and that code works fine too).
Note: I wanted to add "original" to the unversioned upload just so my directory listing looked cleaner. That part could be removed fairly easily.
foo_mobile.jpg
foo_original.jpg
foo_square.jpg
In the current version of CarrierWave if you have an uploader defined like this:
class LogoUploader < CarrierWave::Uploader::Base
# ...
def filename
"original_#{model.logo.file.extension}" if original_filename
end
version :small do
process :resize_to_fit => [190, 190]
process :convert => 'png'
end
version :icon do
process :resize_to_fill => [50, 50]
process :convert => 'png'
end
# ...
end
and attach a file name somefile.jpg, you will end up with files named original.jpg, original_small.png and original_icon.png respectively.
I have issues with translated files in my Rails 4.2 app.
Background
Here are the gem versions I'm using:
gem "rails", "4.2.1"
gem "carrierwave" # 0.10.0
gem "globalize" # 5.0.1
And my model:
class Download < ActiveRecord::Base
belongs_to :download_type
has_and_belongs_to_many :products
translates :title, :part_number, :file
mount_uploader :file, DownloadFileUploader
validates :title, presence: true
def to_param
"#{id}-#{title.parameterize}"
end
end
The Issues
In my view, I want to list a Download and all of the current translations for that download, but all I get is the current locale data for each translation. In the Rails console:
> I18n.locale
=> :en
> download = Download.find(481)
=> #<Download id: 481, title: "SmartSensor HD Quick-reference Guide (User)", part_number: "WX-500-0171", download_type_id: 3, created_at: "2015-01-16 22:49:13", updated_at: "2015-04-20 16:59:25", file: "smartsensor_hd_user_quick-reference_guide-20150116...", download_updated_at: nil>
> download.translations.count
=> 8
> download.translated_locales
=> [:de, :en, :es, :fr, :it, :pt, :ru, :"zh-CN"]
> download.file.class
=> DownloadFileUploader
> download.file.url
=> "/uploads/download/file/481/smartsensor_hd_user_quick-reference_guide-20150116154913-en.pdf"
> download.title
=> "SmartSensor HD Quick-reference Guide (User)"
> download.part_number
=> "WX-500-0171"
And when the locale changes:
> I18n.locale = :de
=> :de
> download.file.class
=> DownloadFileUploader
> download.file.url
=> "/uploads/download/file/481/smartsensor_hd_user_quick-reference_guide-20150116154913-en.pdf"
> download.title
=> "SmartSensor HD Kurzanleitung"
> download.part_number
=> "WX-502-0006"
If I try and access the translation directly:
> I18n.locale = :de
=> :de
> download.translation.file.class
=> String
If I change how the uploader is mounted in my model:
Translation.mount_uploader :file, DownloadFileUploader
Existing translations list correctly—including the file, but I can no longer upload files. What gets stored in the database is this:
/uploads/download/translation/file/401/%23%3CActionDispatch%3A%3AHttp%3A%3AUploadedFile%3A0x007f9c12e6fe00%3E
Notice that it inserts /translation into the path, which I can fix in the uploader, but the filename isn't actually a file.
If I move translates :title, :part_number, :file below mount_uploader :file, DownloadFileUploader in my model, Globalize overrides the mounted uploader and that column is returned as class String when accessing it.
The Cry for Help
Help! 😮
I wrote this simple gem https://github.com/dalpo/carrierwave_globalize. which it should allow to use Globalize and Carrierwave together.
You have to extend your model with the CarrierwaveGlobalize module and use the mount_translated_uploader class method to mount your carrierwave uploader instead of the mount_uploader mehtod.
Follow the instructions in the readme for more info.
Old question, but... do not use Globalize with CarrierWave on the same attribute, they both make overrides for the default behavior.
You can do this in your model:
class Download < ActiveRecord::Base
belongs_to :download_type
has_and_belongs_to_many :products
translates :title, :part_number
mount_uploader :file, DownloadFileUploader
validates :title, presence: true
def to_param
"#{id}-#{title.parameterize}"
end
end
And then override file storing for your uploader in system to use I18n.locale:
def store_dir
"uploads/#{I18n.locale}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
I have Image model:
class Image < ActiveRecord::Base
mount_uploader :file, ModuleImageUploader
end
To upload image I use carrierwave + cloudinary:
class ModuleImageUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
process :resize_to_limit => [700, 700]
version :mini do
process :resize_and_pad => [50, 50, '#ffffff']
end
version :thumb do
process :resize_and_pad => [100, 100, '#ffffff']
end
def public_id
return SecureRandom.uuid
end
end
I created new model AccountMediaContent:
class AccountMediaContent < ActiveRecord::Base
mount_uploader :image, AccountMediaContentImageUploader
end
with it's uploader which also uses carrierwave:
class AccountMediaContentImageUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
process :resize_to_limit => [700, 700]
version :mini do
process :resize_and_pad => [50, 50, '#ffffff']
end
version :thumb do
process :resize_and_pad => [100, 100, '#ffffff']
end
def extension_white_list
%w(jpg jpeg gif png)
end
end
Right now I need to the way to transfer the image from Image to AccountMediaContent. So, that means if I had such file in Image:
http://res.cloudinary.com/isdfldg/image/upload/v1344344359/4adcda41-49c0-4b01-9f3e-6b3e817d0e4e.jpg
Then it means that I need the exact same file in AccountMediaContent so the link to the file will be the same. Is there any way to achieve this?
The optimal solution for this would be to have a new model which represents the image, and then link it to both models.
Ok my solution is not really good but anyway. What I did is I wrote the script which downloaded already existing images Image in Cloudinary and then I attached them to new model AccountMediaContent.
My task looks like this:
Image.find_in_batches do |imgs_in_batch|
imgs_in_batch.each do |img|
# Downloading image to tmp folder (works on heroku too)
file_format = img.file.format
img_url = img.file.url
tmp_file = "#{Rails.root.join('tmp')}/tmp-img.#{file_format}"
File.open(tmp_file, 'wb') do |fo|
fo.write open(img_url).read
end
# Creating AccountMediaContent with old image (it'll be uploaded to cloudinary.
AccountMediaContent.create(image: File.open(tmp_file))
FileUtils.rm(tmp_file)
end
end
Hope it'll be useful for someone.
We have code that looks like run of the mill paper clip:
has_merchants_attached_file :pdf,
storage: :s3,
s3_credentials: Mbc::DataStore.s3_credentials,
s3_permissions: :private,
path: ":identifier_template.pdf",
bucket: Mbc::DataStore.forms_and_templates_bucket_name
validates_attachment_file_name :pdf, :matches => [/pdf\Z/]
Which generates an error:
undefined method `validates_attachment_file_name' for #<Class:0x007fba67d25fe0>
Interestingly enough, when we down grade back to 3.5, we encounter the same issue.
The controller that is generating this is:
def index
#fidelity_templates = FidelityTemplate.order("identifier asc").all
end
Additionally:
def has_merchants_attached_file(attribute, options={})
if Rails.env.test? || Rails.env.development?
has_attached_file attribute,
path: "paperclip_attachments/#{options[:path]}"
else
has_attached_file attribute, options
end
end
Any thoughts on what could be causing this?
You can read about the provided validators here:
https://github.com/thoughtbot/paperclip#validations
The included validators are:
AttachmentContentTypeValidator
AttachmentPresenceValidator
AttachmentSizeValidator
They can be used in either of these ways:
# New style:
validates_with AttachmentPresenceValidator, :attributes => :avatar
# Old style:
validates_attachment_presence :avatar
UPDATE ...
If you read further down the link I've given above you'll get to a section on Security Validations (Thanks Kirti Thorat):
https://github.com/thoughtbot/paperclip#security-validations
They give an example on how to validate the filename format:
# Validate filename
validates_attachment_file_name :avatar, :matches => [/png\Z/, /jpe?g\Z/]
From your code snippet it looks like your validation should work as-is.
However, I've never seen paperclip used with this syntax:
has_merchants_attached_file ...
Perhaps that's the source of your issues? You would usually use the following to attach files to your model:
has_attached_file :pdf ...
I've installed ImageMagick and I've installed the gem Paperclip (version 4.0). I've added:
Paperclip.options[:command_path] = 'C:\Program Files\ImageMagick-6.8.8-Q16'
to the development.rb
My photo.rb Model has this:
has_attached_file :image
validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/png', 'image/jpg']
I can choose a file in photos/new.html.erb but once I click on 'Create photo' button, the page reloads with a Paperclip specific error message saying:
1 error prohibited this photo from being saved:
Image translation missing:
en.activerecord.errors.models.photo.attributes.image.spoofed_media_type
Can someone help please?
Thanks
Add this to an initializer to disable spoofing protection:
require 'paperclip/media_type_spoof_detector'
module Paperclip
class MediaTypeSpoofDetector
def spoofed?
false
end
end
end
That message is raised by a validation check for content spoofing.
For Paperclip v.4 this generates a bug https://github.com/thoughtbot/paperclip/issues/1429
While for Paperclip v.3, it seems it just throws a deprecation warning, https://github.com/thoughtbot/paperclip/issues/1423
So I'd wait for Paperclip team to solve this bug before using version 4. At the moment I'd rather keep using version 3.
gem "paperclip", "~> 3.5.3"
This works on Paperclip v3.5.1 (hopefully will still work in V4):
has_attached_file :attachment,
styles: lambda { |a| a.instance.is_image? ? { *** image_styles ***} : { *** video_styles ***},
processors: lambda { |a| a.is_video? ? [ :ffmpeg ] : [ :thumbnail ] }
def is_video?
attachment.instance.attachment_content_type =~ %r(video)
end
def is_image?
attachment.instance.attachment_content_type =~ %r(image)
end