carrierwave default image update without file select & upload - ruby-on-rails

Many users have asked same question but I am stacked..
Rails : 4.2.5
Carrierwave
Cloudinary
I want to upload an avatar image as default when a user is created.
class UsersController < ApplicationController
after_action :set_default_avatar!, only: [:create]
def create
**************
**************
end
def set_default_avatar!
url = ImageUploader.default_url()
#user.update_attribute(:avatar, url)
end
end
class ImageUploader < CarrierWave::Uploader::Base
def self.default_url()
ActionController::Base.helpers.asset_path("fallback/" + "avatar_#{rand(1..15).to_s}.jpg"])
end
end
once a user creation process is completed, then "after_action" is processed.
"default_url" returns a random image path which is located in 'public/fallbak/'.
I put the url in user.avatar, the users avatar has been updated as NULL.
Neither no image was uploaded on Cloudinary.
Basically, I want to store images at Cloudinary service.
When a user edit users information at a screen, they could be able to upload avatar image.
However I cant upload an image as default avatar by the logic above.
I red the document of Carrierwave, but it doesn't work for me.
I believe that something is missing in my code.
Any help is very appreciated.

It's not uploading the file because ImageUploader.default_url is returning a relative path.
You have two options:
Add a host parameter to your asset_path:
# app/uploaders/image_uploader.rb
def self.default_url()
ActionController::Base.helpers.asset_path("fallback/" + "avatar_#{rand(1..15).to_s}.jpg", host: 'http://example.com')
end
Or use File.open to attach the file from the filesystem:
def set_default_avatar!
path = File.join(Rails.root, 'app/assets/images/fallbak', "avatar_#{rand(1..15).to_s}.jpg")
File.open(path) do |f|
#user.avatar = f
end
end

Related

How do you pass additional variables to a CarrierWave uploader in Rails?

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

Carrierwave + Rails 5: read directory vs store directory

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/..."

Carrierwave - Save nil if the image is not uploaded

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?

Store two versions of a file at once using Carrierwave

I'm using Carrierwave and Fog gems to store a file to my Amazon S3 bucket (to /files/file_id.txt). I need to store a slightly different version of the file to a different location in the bucket (/files/file_id_processed.txt) at the same time (right after the original is stored). I don't want to create a separate uploader attribute for it on the model - is there any other way?
This my current method that stores the file:
def store_file(document)
file_name = "tmp/#{document.id}.txt"
File.open(file_name, 'w') do |f|
document_content = document.content
f.puts document_content
document.raw_export.store!(f)
document.save
end
# I need to store the document.processed_content
File.delete(file_name) if File.exist?(file_name)
end
This is the Document model:
class Document < ActiveRecord::Base
mount_uploader :raw_export, DocumentUploader
# here I want to avoid adding something like:
# mount_uploader :processed_export, DocumentUploader
end
This is my Uploader class:
class DocumentUploader < CarrierWave::Uploader::Base
storage :fog
def store_dir
"files/"
end
def extension_white_list
%w(txt)
end
end
This is how my final solution looks like (kinda) - based on Nitin Verma's answer:
I had to add a custom processor method for the version to the Uploader class:
# in document_uploader.rb
...
version :processed do
process :do_the_replacements
end
def do_the_replacements
original_content = #file.read
File.open(current_path, 'w') do |f|
f.puts original_content.gsub('Apples','Pears')
end
end
considering that you need similar file but with different name.
for this you need to create a version for file in uploader.
version :processed do
process
end
and now second file name will be processed_{origional_file}.extension. if you want to change file name of second file you can use this link https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Customize-your-version-file-names

CarrierWave_Direct gem: Keep current directory structure on S3

so I have a site already in production using the carrierwave gem with images stored on amazon s3. my uploader uses the store_dir method to specify a particular structure to put in processed images.
well, now in my dev environment I've added the carrierwave_direct gem to start uploading directly to S3. The problem is this gem completely overrides the store_dir and filename defaults in my uploader. I can't push my fully working uploader live because all my old image links will be broken.
it's my understanding that the CWdirect gem would upload a raw image file to a "temp" directory on S3, then S3 responds and gives you a key variable so you can grab this file and process it as you see fit. so, should i be using a completely separate image uploader class in carrierwave to process the images and place them in the correct folders? meaning I'll have one uploader dedicated to carrierwave_direct that uploads wherever this gem seems to want to upload to; and I'll use another uploader.rb class linked to my real model that keeps my current store_dir and filename structure?
In any case, my basic question is, how can I use CarrierWave_Direct gem if I already have CW running in production with images in a specific folder structure?
Ok, so I did figure out how to do this and I'll explain below. My hunch was correct to use two different CarrierWave uploader classes--one class dedicated to uploading to S3 (using CarrierWave_Direct gem), and a second class used only for image processing (the class I was already using in production). I'll try and post relevant code below but if anyone has questions let me know. I'm unsure why I haven't seen others using separate classes like this but it seems to work for me.
My image Uploader class app\uploaders\image_uploader.rb utilizing carrierwave_direct gem:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWaveDirect::Uploader
include ActiveModel::Conversion
extend ActiveModel::Naming
include CarrierWave::MimeTypes
process :set_content_type
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg gif png)
end
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
# Override the directory where uploaded files will be stored.
# CarrierWaveDirect::Uploader puts raw uploaded files in this directory on S3 as a first step
def store_dir
"unprocessed_uploads"
end
end
**notice there's no processing being done in this class
My image PROCESSING class app\uploaders\image_processor.rb (what was already in place in production):
class ImageProcessor < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
include CarrierWave::MimeTypes
process :set_content_type
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
# Choose what kind of storage to use for this uploader:
storage :fog
# 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
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
# "uploads/#{model.class.to_s.underscore}/path/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
def default_url
"logos/" + [version_name, "default.png"].compact.join('_')
end
# Process files fetched from S3 after they are uploaded:
def make_thumbnail(width, height)
# uses MiniMagick classes to get a square, centered thumbnail image
manipulate! do |img|
if img[:width] < img[:height]
remove = ((img[:height] - img[:width])/2).round
img.shave("0x#{remove}")
elsif img[:width] > img[:height]
remove = ((img[:width] - img[:height])/2).round
img.shave("#{remove}x0")
end
img.resize("#{width}x#{height}")
img
end
end
# Create different versions of your uploaded files:
# the process statement below isn't defined within a version block on purpose--this means the ORIGINAL uploaded photo is constrained to 1050 pics
process :resize_to_limit => [1050, 1050]
process :quality => 85 # this reduces filesize greatly and saves space
version :thumb do
process :make_thumbnail => [100, 100]
process :quality => 85 # this reduces filesize greatly and saves space
end
version :big_thumb do
process :make_thumbnail => [350, 350]
process :quality => 85 # this reduces filesize greatly and saves space
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg gif png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
def filename
if original_filename
if model && model.read_attribute(:image).present?
model.read_attribute(:image)
else
"#{secure_token}.#{file.extension}"
end
end
end
protected
def secure_token
var = :"##{mounted_as}_secure_token"
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
end
end
My Photo model (summarized):
class Photo < ActiveRecord::Base
mount_uploader :image, ImageProcessor
def save_and_process_image(options = {})
s3_unprocessed_image_url = self.image.asset_host + '/' + self.key
# this next line downloads the image from S3
# and this save line below will process the image and reupload to S3 according to ImageProcessor settings
self.remote_image_url = s3_unprocessed_image_url
save
end
end
I also have Photo controller and view code available, if ya'll want it let me know. Basically I use the ImageUploader class to do the initial upload to S3 to a folder called unprocessed_uploads. Then S3 responds with a key field in the URL which I pass on to ImageProcessor class--this is attached to a Photo and processes the thumbnail and other images then re-uploads them to my uploads folder on S3.
This separation meant i didn't need to change my current folder structure on S3 when adding the carrierwave_direct gem. Hope this helps others. Let me know if you need more code I'm kind of tired of typing :)
UPDATE--Adding more code:
Photos Controller:
class PhotosController < ApplicationController
def index
#photos = #photos.sort_by(&:created_at)
#uploader = ImageUploader.new
#uploader.success_action_redirect = new_tank_photo_url(#tank)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #photos }
end
end
def create
respond_to do |format|
if #photo.save_and_process_image
format.html { redirect_to tank_photos_path(#tank), notice: 'Photo uploaded successfully and is being processed...' }
format.json { render json: #photo, status: :created, location: #photo }
else
format.html { render :new }
format.json { render json: #photo.errors, status: :unprocessable_entity }
end
end
end
Photos Index view, the form with the Upload button:
<%= direct_upload_form_for #uploader, :html => {:class => "form-inline"} do |f| %>
<%= f.file_field :image %>
<%= f.submit "Upload", :class => "btn btn-primary btn-medium" %>
<% end %>
So with the above view/controller code added, here is a summary of the steps taken. Please note the difference between the ImageUploader class and the ImageProcessor class:
In my Photos Controller I create the #uploader variable which is of class ImageUploader--this means that images submitted in my index/view form which uses #uploader will upload the image to a "temp" folder--Since this uses the ImageUploader class then no processing is done when this image is uploaded (yet).
When the user clicks "Upload" button on the form, the Photo>>create action is called. This action calls #photo.save_and_process_image -- look at the model to see that this method actually grabs the recently uploaded image from the S3 'temp' folder, processes it, then re-uploads the processed images to their final destination. This is all possible because my Photo model is linked to the ImageProcessor class, which does the processing/uploading. It's not linked to the separate ImageUploader class.
Hope this helps explain what i have going on

Resources