This gem provides a simple and extremely flexible way to upload files from Ruby applications. It works well with Rack based web applications, such as Ruby on Rails.
Install the latest release:
$ gem install carrierwave
In Rails, add it to your Gemfile:
gem 'carrierwave', '~> 3.0'
Finally, restart the server to apply the changes.
Each release is tested against the versions below. The gemspec only states a minimum, so Bundler will happily install an old CarrierWave alongside a Rails newer than it has ever seen — which is how a patch-level upgrade ends up failing at boot. Check this table when upgrading Rails.
| CarrierWave | Rails | Ruby |
|---|---|---|
| 4.0 | 7.0 – 8.1 | >= 2.7 |
| 3.1 | 6.0 – 8.0 | >= 2.5 |
| 3.0 | 6.0 – 7.1 | >= 2.5 |
| 2.2 | 5.0 – 6.1 | >= 2.2.2 |
Only the latest release gets fixes; older lines are listed so you can tell whether your combination was ever supported.
CarrierWave 3.0 comes with a change in the way of handling the file extension on conversion. This results in following issues if you use process convert: :format to change the file format:
#recreate_versions! to make it usable again.To preserve the 2.x behavior, you can set force_extension false right after calling process convert: :format. See #2659 for the detail.
Start off by generating an uploader:
rails generate uploader Avatar
this should give you a file in:
app/uploaders/avatar_uploader.rb
Check out this file for some hints on how you can customize your uploader. It should look something like this:
class AvatarUploader < CarrierWave::Uploader::Base
storage :file
end
You can use your uploader class to store and retrieve files like this:
uploader = AvatarUploader.new
uploader.store!(my_file)
uploader.retrieve_from_store!('my_file.png')
CarrierWave gives you a store for permanent storage, and a cache for
temporary storage. You can use different stores, including filesystem
and cloud storage.
Most of the time you are going to want to use CarrierWave together with an ORM. It is quite simple to mount uploaders on columns in your model, so you can simply assign files and get going:
Make sure you are loading CarrierWave after loading your ORM, otherwise you'll need to require the relevant extension manually, e.g.:
require 'carrierwave/orm/activerecord'
Add a string column to the model you want to mount the uploader by creating a migration:
rails g migration add_avatar_to_users avatar:string
rails db:migrate
Open your model file and mount the uploader:
class User < ApplicationRecord
mount_uploader :avatar, AvatarUploader
end
Now you can cache files by assigning them to the attribute, they will automatically be stored when the record is saved.
u = User.new
u.avatar = params[:file] # Assign a file like this, or
# like this
File.open('somewhere') do |f|
u.avatar = f
end
u.save!
u.avatar.url # => '/url/to/file.png'
u.avatar.current_path # => 'path/to/file.png'
u.avatar_identifier # => 'file.png'
Note: u.avatar will never return nil, even if there is no photo associated to it.
To check if a photo was saved to the model, use u.avatar.file.nil? instead.
Other ORM support has been extracted into separate gems:
There are more extensions listed in the wiki
CarrierWave also has convenient support for multiple file upload fields.
Add a column which can store an array. This could be an array column or a JSON column for example. Your choice depends on what your database supports. For example, create a migration like this:
rails g migration add_avatars_to_users avatars:json
rails db:migrate
rails g migration add_avatars_to_users avatars:string
rails db:migrate
Note: JSON datatype doesn't exists in SQLite adapter, that's why you can use a string datatype which will be serialized in model.
Leave the column nullable: NULL is what it holds when no file is mounted, so a default: [] or null: false on it doesn't hold — the NULL is written on save. The uploader's own readers answer an empty array either way, and are what to read:
user.avatars # => [] when there is no file
user.avatars_identifiers # => []
user[:avatars] # => nil, the column as it is stored
Open your model file and mount the uploader:
class User < ApplicationRecord
mount_uploaders :avatars, AvatarUploader
serialize :avatars, JSON # If you use SQLite, add this line.
end
Make sure that you mount the uploader with write (mount_uploaders) with s not (mount_uploader)
in order to avoid errors when uploading multiple files
Make sure your file input fields are set up as multiple file fields. For example in Rails you'll want to do something like this:
<%= form.file_field :avatars, multiple: true %>
Also, make sure your upload controller permits the multiple file upload attribute, pointing to an empty array in a hash. For example:
params.require(:user).permit(:email, :first_name, :last_name, {avatars: []})
Now you can select multiple files in the upload dialog (e.g. SHIFT+SELECT), and they will automatically be stored when the record is saved.
u = User.new(params[:user])
u.save!
u.avatars[0].url # => '/url/to/file.png'
u.avatars[0].current_path # => 'path/to/file.png'
u.avatars[0].identifier # => 'file.png'
If you want to preserve existing files on uploading new one, you can go like:
<% user.avatars.each do |avatar| %>
<%= hidden_field :user, :avatars, multiple: true, value: avatar.identifier %>
<% end %>
<%= form.file_field :avatars, multiple: true %>
Sorting avatars is supported as well by reordering hidden_field, an example using jQuery UI Sortable is available here.
In order to change where uploaded files are put, just override the store_dir
method:
class MyUploader < CarrierWave::Uploader::Base
def store_dir
'public/my/upload/directory'
end
end
This works for the file storage as well as Amazon S3 and Rackspace Cloud Files.
Define store_dir as nil if you'd like to store files at the root level.
With the :file storage, if you store files outside the project root folder, you may want to define cache_dir in the same way:
class MyUploader < CarrierWave::Uploader::Base
def cache_dir
'/tmp/projectname-cache'
end
end
To change the filename of uploaded files, you can override #filename method in the uploader.
class MyUploader < CarrierWave::Uploader::Base
def filename
"image.#{file.extension}" # If you upload 'file.jpg', you'll get 'image.jpg'
end
end
The name is worked out again whenever the stored file is located, so it has to come out the same every time. Memoise anything that would otherwise differ per call, in an instance variable of your own, as @filename is the uploader's:
def filename
@stable_filename ||= "#{SecureRandom.uuid}.#{file.extension}"
end
Some old documentations (like this) may instruct you to safeguard the filename value with if original_filename, but it's no longer necessary with CarrierWave 3.0 or later.
Certain files might be dangerous if uploaded to the wrong location, such as PHP files or other script files. CarrierWave allows you to specify an allowlist of allowed extensions or content types.
If you're mounting the uploader, uploading a file with the wrong extension will make the record invalid instead. Otherwise, an error is raised.
class MyUploader < CarrierWave::Uploader::Base
def extension_allowlist
%w(jpg jpeg gif png)
end
end
The same thing could be done using content types. Let's say we need an uploader that accepts only images. This can be done like this
class MyUploader < CarrierWave::Uploader::Base
def content_type_allowlist
/image\//
end
end
You can use a denylist to reject content types. Let's say we need an uploader that reject JSON files. This can be done like this
class NoJsonUploader < CarrierWave::Uploader::Base
def content_type_denylist
['application/text', 'application/json']
end
end
| 日期 | 榜单 | 排名 | 周期新增 | 总 STAR |
|---|