i am trying to migrate images from local file system to dropbox, so i am using carrierwave dropbox gem to move all images to dropbox. i am able to store new images which is uploaded from my application. I am trying to move the existing images.
i am using Article.first.avatar? method to check whether the image exists or not, i have used this method in many places for different sizes of images in my application.
when i use the above method to find out whether the image exists or not, it says true always when the image is not present in dropbox. Look at my console output(2),
My uploader:
class Avatar < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage: file
def store_dir
if model
"uploads/#{model.class.to_s.underscore}/#{model.id}/#{mounted_as}"
else
"uploads/#{mounted_as}/"
end
end
end
console output (1)
>Article.first.avatar?
>false
#<AvatarUploader:0x007f9813f1fe70
#file=
#<CarrierWave::SanitizedFile:0x007f9813f1e688
#content_type=nil,
#file="/Users/work/project/app1/public/uploads/370/avatar/avatar.png",
#original_filename=nil>,
#model=
##Article model
#mounted_as=:avatar,
#storage=#<CarrierWave::Storage::File:0x007f9813f1fad8 #uploader=#<AvatarUploader:0x007f9813f1fe70 ...>>,
#versions={}>
I changed uploader as follows:
class Avatar < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage: dropbox
def store_dir
if model
"uploads/#{model.class.to_s.underscore}/#{model.id}/#{mounted_as}"
else
"uploads/#{mounted_as}/"
end
end
end
console output(2)
>Article.first.avatar?
>true
#<AvatarUploader:0x007f8574143ee8
#file=
#<CarrierWave::Storage::Dropbox::File:0x007f8574143308
#client=
#<DropboxClient:0x007f8574143420
#root="dropbox",
#session=
#<DropboxSession:0x007f8574143498
#access_token=#<OAuthToken:0x007f8574143470 #key="123453333", #secret="22222222222">,
#consumer_key="abcdeafs",
#consumer_secret="asdfasfj",
#locale=nil,
#request_token=nil>>,
#config=
{:app_key=>"asdfasfasf",
:app_secret=>"asdfkasfksf",
:access_token=>"adfkjasfkhs",
:access_token_secret=>"aksdfkhsfksf",
:access_type=>"dropbox",
:user_id=>"292929292"},
#path="uploads/images/370/avatar.png",
#uploader=#<AvatarUploader:0x007f8574143ee8 ...>>,
#model=
#Artcle Model>,
#mounted_as=:image,
#storage=
#<CarrierWave::Storage::Dropbox:0x007f8574143c90
#config=
{:app_key=>"asdfasfasf",
:app_secret=>"asdfkasfksf",
:access_token=>"adfkjasfkhs",
:access_token_secret=>"aksdfkhsfksf",
:access_type=>"dropbox",
:user_id=>"292929292"},
#dropbox_client=
#<DropboxClient:0x007f8574143420
#root="dropbox",
#session=
why does it show "true" when the image is not present.
how can i get "false" when the image is not present in dropbox.
My stab in the dark is that inside Carrierwave, the normal file class it returns (sanitized file) responds to empty by checking if the file exists:
https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/carrierwave/sanitized_file.rb#L144
Inside the carrierwave dropbox gem, the File class (https://github.com/robin850/carrierwave-dropbox/blob/master/lib/carrierwave/storage/dropbox.rb#L45) doesn't implement this. I'm guessing if you reopened the class and added
module CarrierWave
module Storage
class Dropbox < Abstract
class File
def empty?
# use dropbox API to get the file and check the presence here
end
end
end
end
end
It would probably work?
Related
I see this has been asked a few times over the years (eg Upload path based on a record value for Carrier wave Direct, Passing a parameter to the uploader / accessing a model's attribute from within the uploader / letting the user pick the thumbnail size), but I'm convinced I must be overcomplicating this, as it seems like a very simple problem...
I have a very straightforward Video model that mounts an uploader:
class Video < ApplicationRecord
mount_uploader :file, VideoUploader
end
In the controller, I allow two parameters:
def video_params
params.require(:video).permit(:title, :file)
end
In the actual VideoUploader, I seem to have access to a number of variables derived from the :file column using class builtins (eg original_filename), and I can process the file using ffmpeg parameters. However, I want the parameters to be conditional based on the :title string, and I have no idea how to scope it or access it. What is the absolute simplest way to make sure this variable is accessible to those methods?
Edit: here's the uploader code:
class VideoUploader < CarrierWave::Uploader::Base
require 'streamio-ffmpeg'
include CarrierWave::Video
case #title # not working
when "tblend_glitch"
process encode_video: [:mp4,
resolution: "1280x960",
custom: %w(-to 5 -vf scale=-2:720,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference)]
...
end
def full_filename(for_file)
super.chomp(File.extname(super)) + '.mp4'
end
def filename
original_filename.chomp(File.extname(original_filename)) + '.mp4'
end
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
Thanks!
You should be able to access the instance in the uploader using the model method.
You haven't defined #title — it's nil. You can create a conditional version with the following code.
class VideoUploader < CarrierWave::Uploader::Base
version :tblend, if: :tblend_glitch? do # use a better version name
process encode_video: [:mp4,
resolution: "1280x960",
custom: %w(-to 5 -vf scale=-2:720,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference)]
end
# rest of the code
private
def tblend_glitch?
model.title == 'tblend_glitch'
end
end
Ref: https://github.com/carrierwaveuploader/carrierwave#conditional-versions
In theory you can access the model and its attributes from the uploader. However, it looks like the mounted uploader gets invoked before the other attributes are assigned.
For me it worked to create the model with the regular parameters first, and assign the attribute that has the uploader mounted (in your case :file) in a second step. Then I could read all model attributes correctly from within the uploader.
In your case that would be something like this in the controller:
#video = Video.new(video_params.except(:file))
#video.file = video_params[:file] # Invoke uploader last to access the other attributes
I've introduced a new version on my Carrierwave Uploader. When I create a new Event it creates both versions correctly. But when I update it, only the file I attached gets uploaded, but versions do not get recreated.
I am using CarrierWave 1.2.2, and looking at the changelog, it doesn't seem to have been a bug that got fixed in the newer versions
class CoverUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
if Rails.env.development? || Rails.env.test?
storage :file
elsif Rails.env.production?
storage :fog
end
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
if ENV['HEROKU_APP_NAME'].to_s.include?('-pr-')
"review_apps/#{model.class.to_s.underscore}/#{model.id}"
else
"#{Rails.env}/#{model.class.to_s.underscore}/#{model.id}"
end
end
# Provide a default URL as a default if there hasn't been a file uploaded:
def default_url(*args)
ActionController::Base.helpers.asset_path('test.jpg')
end
# Create different versions of your uploaded files:
version :optimised do
process convert: 'webp'
process :set_content_type_to_webp
def full_filename(_for_file = model.cover.file)
"cover_#{model.id}.webp"
end
def exists?
file&.exists?
end
end
def extension_blacklist
%w(webp)
end
private
# Required to actually force Amazon S3 to treat it like an image
def set_content_type_to_webp
file.instance_variable_set(:#content_type, 'image/webp')
end
end
#ogelacinyc was partly correct when he found the bug in full_filename. I went back to test normal functionality with creating another version, with a simple dimension change. I could then see that update would recreate the versions by itself, just like I expected.
That made me think that maybe there is something wrong with my version :optimised block. So after commenting one by one, I found that full_filename was the culprit. It could have been model.cover.file failing silently, but I think it was model.id, as can be seen in the description for filename method in Carrierwave
So instead, I grab the filename directly, extract extension and substitute it with webp:
def full_filename(for_file = model.file_name.file)
extension = File.extname(for_file)
"cover_#{for_file.sub(extension, '.webp')}"
end
Which works without problems!
You need to add an after_save callback to Event and then call recreate_versions! on your mounted uploader.
Assuming you have an Event model with the following, this would solve your problem.
class Event < ApplicationRecord
mount_uploader :cover_image, CoverUploader
after_save :recreate_versions!
delegate :recreate_versions!, to: :cover_image, allow_nil: true
end
See CarrierWave's README also.
I have been struggling to find any tutorial or question that explains how to upload image and resize it according to certain conditions provided by user.
I am easily able to upload and resize image using hard coded values, however I am stuck at using user provided parameters to be accessed from the Uploader.
I want the image to be resized to either 800x600 or 300x300 based on whether the user checks the image as Large or Small.
For that I have a boolean column named "large" in the model structure.
In the Uploader I am able to access model and its values easily in the store_dir block, but anywhere outside this block any model attribute returns as nil.
This is what I want to do:-
class BannerUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
resize_to_fit(800,600) if model.large==true
resize_to_fit(300,300) if model.large!=true
end
However this returns the error
undefined local variable or method `model' for BannerUploader:Class
How to go about this issue.
To process an original file you can specify custom method:
class BannerUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
process :process_original_version
def process_original_version
if model.large
resize_to_fit(800,600)
else
resize_to_fit(300,300)
end
end
end
For a specific version:
class BannerUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :normal do
if model.large
process resize_to_fit: [800,600]
else
process resize_to_fit: [300,300]
end
end
end
Ok so #Alex Kojin has the right answer. However I was faced with another problem as well. When the user submitted the form, the image would be resized always as small (300x300) because for some reason the image resize process would be executed first and then the "large" attribute would be set as true. Therefore the Uploader would always get model.large as false.
So this is how I had to change my action controller
def create
#banner=Banner.new
#banner.large=params[:large]
#banner.update_attributes(banner_params)
#banner.save
redirect_to :back
end
Not sure if this is the right approach but does work for me.
Carrierwave's enter link description here gives both the directory where Carrierwave uploads files and the directory where Carrierwave looks for files. However, I'd like to know if there's a way to define something like a read_dir, which would be the path where Carrierwave looked for files, and leave the store_dir only for storing files.
I know this probably doesn't make much practical sense, but I'd just like to know.
You can calculate store_dir based on some condition of the object. You can also set this condition when you need a different directory. For example:
class MyModel < ActiveRecord::Base
attr_accessible :use_directory_for_storing
mount_uploader :file, MyFileUploader
end
class MyFileUploader < CarrierWave::Uploader::Base
# ...
def store_dir
if model.use_directory_for_storing
"some/directory/for/storing"
else
"some/directory/for/reading"
end
end
end
# Usage
object = MyModel.new(params)
object.use_directory_for_storing = true
object.save # the file will be stored in ".../some/directory/for/storing/" directory
object = MyModel.last
# will look for the file in the ".../some/directory/for/reading/" directory
object.file.path # => ".../some/directory/for/reading/..."
I am currently using carrierwave, and i am wondering if the following his possible. If so how!. Thanks in advance
I have a user and wants to upload an avatar then the following folder would be created
public/image/avatar/customer.id/image01/small
public/image/avatar/customer.id/image01/normal
public/image/avatar/customer.id/image01/big
public/image/avatar/customer.id/image02/small
public/image/avatar/customer.id/image02/normal
public/image/avatar/customer.id/image02/big
Basically, I do not want to overide the previous image has i want to keep them, but create a folder for the newest picture to have it there. Also want the customer id has a path.
Thanks.
PS: if its possible, please provide a tutorial or somesort, if not possible would paperclip allow it? Thanks i can't seem to find anything about it.
uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"public/image/avatar/#{current_user.id}"
end
version :small do
end
version :normal do
end
version :big do
end
end
Your image uploader should look something like that.