I'm having a problem uploading multiple images via Carrierwave and am not sure if it's a bug or user error (probably the latter). I'm doing everything in a rather standard way though (as per documentation) so it's weird that this doesn't work.
I have the following in my Gemfile:
gem 'carrierwave', github: 'carrierwaveuploader/carrierwave'
my image_uploader.rb:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process :resize_to_limit => [590, 590]
end
version :featured do
process :resize_to_fill => [390, 390]
end
def extension_white_list
%w(jpg jpeg gif png)
end
end
My article.rb file:
class Article < ActiveRecord::Base
mount_uploaders :images, ImageUploader
end
I am letting the params pass from my controller with:
def article_params
params.require(:article).permit(:title, :images, :body)
end
And the _form.html.erb partial uses:
<%= form_for #article, html: { multipart: true } do |f| %>
<%= f.label :images %><br>
<%= f.file_field :images, multiple: true %><br>
<%= f.submit 'Update Article' %>
<% end %>
Oddly, when I upload two images, I am not seeing them passed into the article_params from the update method.
If I pry it, I can see that:
params.require(:article).permit(:images)
Unpermitted parameters: title, images, body
=> {}
Images seems to be unpermitted, even though I explicitly permit it...
Any clue as to what may be incorrect here?
The solution seems to be:
params.require(:article).permit(:title, :body, images: [])
If you take a look in pry on images param, its type is an Array:
> params[:article][:images].class
=> Array
From docs:
To declare that the value in params must be an array of permitted scalar values map the key to an empty array:
params.permit(id: [])
Source: 4.5.1 Permitted Scalar Values
Related
I've run into some issues when trying to allow the upload of an avatar with Shrine. I'm using Rails 5. I continually get the error, "undefined method `cached_image_data' for nil:NilClass".
I've tried restarting several times, and following along with various tutorials. As far as I can tell, I am doing everything I'm meant to. ImageUploader is set, my Photo model is set etc. I've included the relevant bits of code below.
apps/models/photo.rb:
class Photo < ApplicationRecord
include ImageUploader::Attachment.new(:image)
end
apps/uploaders/image_uploader.rb
require "image_processing/mini_magick"
class ImageUploader < Shrine
plugin :processing
plugin :versions, names: [:original, :thumb, :medium]
plugin :delete_raw # delete processed files after uploading
process(:store) do |io, context|
original = io.download
pipeline = ImageProcessing::MiniMagick.source(original)
size_80 = pipeline.resize_to_limit!(80, 80)
size_300 = pipeline.resize_to_limit!(300, 300)
original.close!
# return hash of 3 sizes of the same image
{ original: io, thumb: size_80, medium: size_300 }
end
end
app/views/profiles/_form.html.erb
<%= form_with(model: profile, local: true) do |form| %>
<div class="field">
<%= form.label :image %>
<%= form.hidden_field :image, value: #photo.cached_image_data %>
<%= form.file_field :image, id: :photo_image_data %>
</div>
<% end %>
app/controllers/profile_controller.rb
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
before_action :view_own_profile, only: [:show]
def show
end
private
def set_profile
#profile = Profile.find(params[:id])
end
def profile_params
params.require(:profile).permit(:first_name, :last_name, :mobile,
:street_address, :suburb, :postcode, :country, :mobile, :image,
:latitude, :longitude, :user_id)
end
end
Any help at all would be greatly appreciated. Please let me know if there's any other information needed.
Cheers,
Gen
You should set the #photo variable in the controller. If it can't be set because it doesn't exist yet you might want to call #photo&.cached_image_data (notice the &) in your form instead. This is the same as calling #photo.nil? ? nil : #photo.cached_image_data which checks if #photo is not nil before calling #cached_image_data. If #photo is nil the value of nil is used instead and #cached_image_data will never be called.
See the "safe navigation operator" section of the 2.3.0 release notes.
Though what 3lmin4t0r wrote is right in this case there is a more simple solution.
In the file apps/uploaders/image_uploader.rb you need to add the line
plugin :cached_attachment_data
To the list of plugins.
I'm currently implementing CarrierWave into my app. A problem I noticed is that most images I've uploaded fail to correctly generate a thumbnail size (64x64).
I'm using resize_to_fit and resize_to_fill for different versions, and the resize_to_fill (for cropping) is failing most of the time.
Here I've generated two versions, gallery and thumb.
// image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :gallery do
process resize_to_fit: [400, 300]
end
version :thumb do
process :resize_to_fill => [64, 64]
end
def extension_whitelist
%w(jpg jpeg gif png)
end
end
They look correct; however, if you actually open thumb within Paint, it's completely blank.
I've tested this with a bunch of different pictures, and most have this happen. But I did notice that if I open the main image, save it (without doing anything), then recreate the versions, it's fixed. Must be something with how it's being saved, or something.
Here is the source image that I've been able to reproduce this issue with.
Thanks.
Very strange behavior. I was very curious about this problem, so I made very simple example on my dev. machine referencing Ryan's Bates
cropping images episode.
You can just follow up, it may help you.
Let's say we generated simple user scaffold, and we have in our DB fields called :name and :avatar of string types.
In our User model we'll mount uploader and create virtual attributes:
class User < ApplicationRecord
mount_uploader :avatar, AvatarUploader
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
end
Method create in users_controller.rb will look like this (including strong params):
def create
#user = User.new(user_params)
if #user.save
if params[:user][:avatar].present?
render :crop # Render crop template
else
redirect_to #user, notice: "Successfully created user."
end
else
render :new
end
end
# Strong params# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit!
end
The image_uploader.rb you posted was good. I will just post mine, as I made large and thumb versions and used resize_to_limit process
instead of resize_to_fit:
version :large do
resize_to_limit(400, 300)
end
version :thumb do
resize_to_fill(64, 64)
end
We can create now crop.html.erb template in views/users folder and populate with code below:
<h1>Crop Avatar</h1>
<%= image_tag #user.avatar_url(:large) %>
<h4>Preview</h4>
<div style="width:100px; height:100px; overflow:hidden">
<%= image_tag #user.avatar.url(:large) %>
</div>
<%= form_for #user do |f| %>
<% %w[x y w h].each do |attribute| %>
<%= f.hidden_field "crop_#{attribute}" %>
<% end %>
<div class="actions">
<%= f.submit "Crop" %>
</div>
<% end %>
When you create new user form it will have avatar attachment. When you upload photo and submit it, it will trigger
def create method and it will check if params are presented, and render the crop.html.erb template:
if params[:user][:avatar].present?
render :crop # Render crop template
else
I haven't included in this example Jcrop library for cropping images, this was very simple example. At the end it should crop 64x64 pixels
by default.
There is also CarrierWave extension to crop uploaded images using Jcrop plugin with preview, which I haven't tried.
I hope it helps
my plan is i want to create a model only for images and use that model to create profile pictures, gallery etc..
so, i have created a separate model for images and i was able to store images in display those images. but, they are too big and so i want to create a thumbnail version.
my configuration is
class Image < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
controller:
class ImagesController < ApplicationController
def new
#image_upload=Image.new
end
def create
#image_upload=Image.create(uploading_image)
if #image_upload.save
redirect_to '/users'
end
end
def uploading_image
params.require(:image).permit(:avatar)
end
end
uploader:
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}...(default store only)"
end
version :thumb do
process :resize_to_limit => [50, 50]
end
user controller:
def index
#user_profile=Profile.find(1)
#imagefile=Image.first
end
users/index.html.erb:
<%= image_tag #imagefile.avatar.to_s %> #This gives me the whole image.
<%= image_tag #imagefile.image_url(:thumb).to_s %> #says undefined method `image_url' for #<Image:0x007f6cd0538010>
ps: i can see a thumbnail version in the folder
It would be so great, if anyone could help me out.
You call your other versions of the #imagefile the same way you call its original copy, just add the version name. And you can use the url helper. so:
<%= image_tag #imagefile.avatar.url %>
<%= image_tag #imagefile.avatar.thumb.url %>
<%= image_tag #imagefile.image_url(:thumb) %> should be <%= image_tag #imagefile.avatar_url(:thumb) %> as the field that has an uploader mounted is called 'avatar'. Also you shouldn't need to call .to_s on the url.
I'm trying to upload images through carrier wave, and using rails.
using minimagick.
Actually, the uploads working properly I think, but images are not shown up.
Also it shows nil value in database.
here's my code.
app/models/user.rb
class User < ActiveRecord::Base
mount_uploader :image, ImageUploader
end
mypage.html.erb (form action to "edit_complete")
<input type="file" name='image'>
users_controller.rb
def edit_complete
user = User.find(session[:user_id])
user.image = params[:image]
user.save
redirect_to :back
end
app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
No errors appeared, but nothing saved in database.
I expect image files should be saved in public/upload folder, and image file should be shown in database in rails c User.all.
However, when I upload 1.png file,
there are nothing saved, and public/upload folder is not generated.
Also in database shows nil value.
Is there any solution for this?
In file upload You need to use multipart option in form tag ie multipart option should be true in form tag.
Try something like this:
<%= form_for #user, :html => {:multipart => true} do |f| %>
<%= f.file_field :image%>
<%= f.submit 'Edit'%>
<% end %>
I had a quick question. Is it possible to save a file without actually uploading it through a form?
For example, let's say I'm looking at attachments from emails, and I want to save them using a paperclip. How do I do this? Do I manually have to call a save_file(or something similar) somewhere?
Any help would be much appreciated!
I have a rake task that loads images (client logos) from a directory directly onto parperclip. You can probably adapt it to your needs.
This is my simplified Client model:
class Client < ActiveRecord::Base
LOGO_STYLES = {
:original => ['1024x768>', :jpg],
:medium => ['256x192#', :jpg],
:small => ['128x96#', :jpg]
}
has_attached_file :logo,
:styles => Client::LOGO_STYLES,
:url => "/clients/logo/:id.jpg?style=:style"
attr_protected :logo_file_name, :logo_content_type, :logo_size
Then on my rake task I do this:
# the logos are in a folder with path logos_dir
Dir.glob(File.join(logos_dir,'*')).each do |logo_path|
if File.basename(logo_path)[0]!= '.' and !File.directory? logo_path
client_code = File.basename(logo_path, '.*') #filename without extension
client = Client.find_by_code(client_code) #you could use the ids, too
raise "could not find client for client_code #{client_code}" if client.nil?
File.open(logo_path) do |f|
client.logo = f # just assign the logo attribute to a file
client.save
end #file gets closed automatically here
end
end
Regards!
The file saved in Paperclip doesn't have to be uploaded directly through a form.
I'm using Paperclip in a project to save files from URLs from webcrawler results. I'm not sure how you'd get email attachments (are they on the local file system of the server? Is your app an email app like GMail?) but as long as you can get a file stream (via something like open(URI.parse(crawl_result)) in my case...) you can attach that file to your model field that's marked has_attached_file.
This blog post about Easy Upload via URL with Paperclip helped me figure this out.
Since it now appears the original blog post is no longer available - here's the gist of it pulled from wayback machine:
This example shows a Photo model that has an Image attachment.
The technique we're using requires adding a *_remote_url (string) column for your attachment, which is used to store the original URL. So, in this case, we need to add a column named image_remote_url the photos table.
# db/migrate/20081210200032_add_image_remote_url_to_photos.rb
class AddImageRemoteUrlToPhotos < ActiveRecord::Migration
def self.up
add_column :photos, :image_remote_url, :string
end
def self.down
remove_column :photos, :image_remote_url
end
end
Nothing special is required for the controller...
# app/controllers/photos_controller.rb
class PhotosController < ApplicationController
def create
#photo = Photo.new(params[:photo])
if #photo.save
redirect_to photos_path
else
render :action => 'new'
end
end
end
In the form, we add a text_field called :image_url, so people can upload a file or provide a URL...
# app/views/photos/new.html.erb
<%= error_messages_for :photo %>
<% form_for :photo, :html => { :multipart => true } do |f| %>
Upload a photo: <%= f.file_field :image %><br>
...or provide a URL: <%= f.text_field :image_url %><br>
<%= f.submit 'Submit' %>
<% end %>
The meaty stuff is in the Photo model. We need to require open-uri, add an attr_accessor :image_url, and do the normal has_attached_file stuff. Then, we add a before_validation callback to download the file in the image_url attribute (if provided) and save the original URL as image_remote_url. Finally, we do a validates_presence_of :image_remote_url, which allows us to rescue from the many exceptions that can be raised when attempting to download the file.
# app/models/photo.rb
require 'open-uri'
class Photo < ActiveRecord::Base
attr_accessor :image_url
has_attached_file :image # etc...
before_validation :download_remote_image, :if => :image_url_provided?
validates_presence_of :image_remote_url, :if => :image_url_provided?, :message => 'is invalid or inaccessible'
private
def image_url_provided?
!self.image_url.blank?
end
def download_remote_image
self.image = do_download_remote_image
self.image_remote_url = image_url
end
def do_download_remote_image
io = open(URI.parse(image_url))
def io.original_filename; base_uri.path.split('/').last; end
io.original_filename.blank? ? nil : io
rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
end
end
Everything will work as normal, including the creation of thumbnails, etc. Plus, since we're doing all of the hard stuff in the model, "uploading" a file via URL works from within script/console as well:
$ script/console
Loading development environment (Rails 2.2.2)
>> Photo.new(:image_url => 'http://www.google.com/intl/en_ALL/images/logo.gif')
=> #<Photo image_file_name: "logo.gif", image_remote_url: "http://www.google.com/intl/en_ALL/images/logo.gif">