After reviewing both libraries I went with CarrierWave for this project as it seemed the least obtrusive and most flexible. CarrierWave can be used with the file system, Amazon S3 and a database including Mongo GridFS. Since my project is hosted on Cloud Foundry and that gives me free access to install Mongo and bind it to my Application so I decided to try that option.
The first task for which I wanted to add images was when users registered on my app using their Facebook account. Here are the steps you can take to support uploading and serving images. For more details on the Facebook integration review the user.rb model in the source code.
to contain:
Add a string column to the model you want to mount the uploader on:
Open your model file and mount the uploader:
The first task for which I wanted to add images was when users registered on my app using their Facebook account. Here are the steps you can take to support uploading and serving images. For more details on the Facebook integration review the user.rb model in the source code.
Steps on your terminal
This assumes you already have a Ruby on Rails 3.0 application on Cloud Foundry with a Users model# Log in to cloud foundry if you are not logged in
vmc login youremail@website.com
vmc create-service mongodb
# See what the newly created mongo service is called
vmc services
# Bind the service to your existing Application
vmc bind-service mongodb-???? appname
Steps on your Code base
1- Add gems to your Gemfile
gem 'carrierwave'
gem 'carrierwave-mongoid', :require => "carrierwave/mongoid"
2- Install CarrierWave for your Model
rails generate uploader Avatar
3 - Edit the generated file
app/uploaders/avatar_uploader.rbto contain:
class AvatarUploader < CarrierWave::Uploader::Base
# Choose what kind of storage to use for this uploader:
storage :grid_fs
# 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
"#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
def default_url
"/images/fallback/" + [version_name, "default.png"].compact.join('_')
end
end
4- Update your ActiveRecord model to store the avatar
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_column :users, :avatar, :string
class User
mount_uploader :avatar, AvatarUploader
# Make sure that the avatar is accessible
attr_accessible :avatar, :remote_avatar_url, :email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :display_name, :username ...
.
end
5 - Create an initializer for Mongoid to use your Mongo DB instance on Cloud Foundry
- Name it 01_mongoid.rb so it runs before everything else
Mongoid.configure do |config|
conn_info = nil
if ENV['VCAP_SERVICES']
services = JSON.parse(ENV['VCAP_SERVICES'])
services.each do |service_version, bindings|
bindings.each do |binding|
if binding['label'] =~ /mongo/i
conn_info = binding['credentials']
break
end
end
end
raise "could not find connection info for mongo" unless conn_info
else
conn_info = {'hostname' => 'localhost', 'port' => 27017}
end
cnx = Mongo::Connection.new(conn_info['hostname'], conn_info['port'], :pool_size => 5, :timeout => 5)
db = cnx['db']
if conn_info['username'] and conn_info['password']
db.authenticate(conn_info['username'], conn_info['password'])
end
config.master = db
end
6 - Update your CarrierWave Initializer to use the Cloud Foundry Mongo DB
#initializers/carrierwave.rb
require 'serve_gridfs_image'
CarrierWave.configure do |config|
config.storage = :grid_fs
config.grid_fs_connection = Mongoid.database
# Storage access url
config.grid_fs_access_url = "/grid"
end
7- Handle requests for the images in lib/serve_gridfs_image.rb
class ServeGridfsImage
def initialize(app)
@app = app
end
def call(env)
if env["PATH_INFO"] =~ /^\/grid\/(.+)$/
process_request(env, $1)
else
@app.call(env)
end
end
private
def process_request(env, key)
begin
Mongo::GridFileSystem.new(Mongoid.database).open(key, 'r') do |file|
[200, { 'Content-Type' => file.content_type }, [file.read]]
end
rescue
[404, { 'Content-Type' => 'text/plain' }, ['File not found.']]
end
end
end
Step 8 - Deploy !
bundle install
bundle package
vmc update app_name
Conclusion
This will give you the ability to upload and serve images. Do note that this will not provide image resizing. If you are using devise for example you can import the avatar(profile picture) of the user when they sign up.class << self
def new_with_session(params, session)
super.tap do |user|
if session['devise.omniauth_info']
if data = session['devise.omniauth_info']['user_info']
user.display_name = data['name'] if data.has_key? 'name'
user.email = data['email']
user.username = data['nickname'] if data.has_key? 'nickname'
user.first_name = data['first_name'] if data.has_key? 'first_name'
user.last_name = data['last_name'] if data.has_key? 'last_name'
user.remote_avatar_url = data['image'] if data.has_key? 'image'
end
end
end
end
end
References
- http://support.cloudfoundry.com/entries/20016922-connecting-to-a-mongo-db
- https://github.com/jnicklas/carrierwave
- https://github.com/jnicklas/carrierwave-mongoid
- http://antekpiechnik.com/posts/setting-up-carrierwave-file-uploads-using-gridfs-on-rails-3-and-mongoid
- https://github.com/plataformatec/devise