蓝天,小湖,湖水中一方小筑

Uploading multiple attachments with carrierwave/mongoid/nested_form

Recently I have met a requirement that need to upload multiple attachments to a rails project. After some investigations, I choose carrierwave finally. Also, I selected nested_form to manage uploading and deleting multiple attachments.

Gemfile

First thing to use those gems is updating Gemfile in the project, so those lines have been added to the file:

# Mongoid
gem "mongoid"

# File upload
gem 'carrierwave'
gem 'carrierwave-mongoid', :require => 'carrierwave/mongoid'
gem "mini_magick"

# Form for multi model
gem 'nested_form'

After the modification, remember to run bundle update to update/install the gems and dependencies.

Model

The next step is creating the models. For simplicity, there are only two models in the project. One model named Foo, which contains a title field and a relationship field with the other model FooImage. Here is the definition of model Foo.

class Foo
  include Mongoid::Document

  field :title, type: String

  has_many :foo_images # Photoes of the dish
  accepts_nested_attributes_for :foo_images, :allow_destroy => true
end

The accepts_nested_attributes_for keyword enables saving associated records though the parent, while the :allow_destroy parameter allows deleting associated though attributes hash.

Here comes the FooImage model:

class FooImage
  include Mongoid::Document

  attr_accessible :image
  mount_uploader :image, FooUploader

  belongs_to :foo
end

This model only contains one field :image, and the difference between others is the mount_uploader keyword, which is used to mount given uploader to the given column, then assigning and reading from this field will upload and retrieve files. The uploader is introduced by carrierwave, which will be introduced in next section.

Uploader

Uploader is used to handle the file uploaded to the server, which will save the file to specified location with multiple version. A uploader can be created by the command listed below:

rails g uploader Foo

A new uploader foo_uploader.rb will be generated under directory app/uploaders/ after command return. The uploader generated by default can handle file uploading, and you can add some other function, such as scaling image, set whitelisted extension, in the uploader. Remember to remove the comment character from either CarrierWave::RMagick or CarrierWave::MiniMagick line to enable scaling function. Carrierwave alwo support uploading file to cloud storage such as s3 directly, please refer to the document for detail. In this uploader, I scale the uploaded image to 800x600 and add a thumbnail version with size 80x60, here is the code:

process :resize_to_fill => [800, 600]

version :thumb do
  process :resize_to_fill => [80, 60]
end

NOTICE: The generated uploader uses scale for resizing, remember to replace it with existing function like resize_to_fill.

Views

The last thing is the view. I plan to use two views in the project. Since this is just a simple demo, I have only created one record and shown the title and images with table.

By the way, if you want to deploy this application on production environment, please make sure those two configuration:

  • Make sure your web server, such as Nginx, Apache, has write permission to its temp path. If you don’t know which directory is it, you can use this simple method to determine: First, check whether the production server can complete process of uploading attachment (Just uploading only, showing of image may have another problem which will be talked about below ). If there is no error, then the permission issue has already done. But if error occurred, please refer to the error log or access log of web server for tracing.

  • Now the second problem, showing the image. After uploading successfully, you may can’t see the uploaded file. The problem is because production server do not serve static files. The quick fix for this is changing line config.serve_static_assets = false to true, but it is not the recommended way, since the production server can’t server static file efficiently. The better method is configuring web server to serve those static file directly, please refer to web server’s manual for detail.

So, that’s it. Thanks for reading, and here is the link of project on GitHub, any comments are appreciated.