Multiple Images Uploading With CarrierWave and PostgreSQL Array
Notice: there are two known issues with this approach, see issue 1 and issue 2.
And Your mileage may vary.
Notice 2: if you counter last image deletion bug or all images deletion issue, refer to this github issue for fix.
TL;DR. Jump to fully completed sample codes on Github.
In this blog post I am going to demonstrate how to implement multiple images uploading related features by using CarrierWave and PostgreSQL Array, and I am going to illustrate the technique in the context of a sample gallery application.
To be specific about the features, we are going to implement the following ones:
- Create a new gallery by uploading multiple images
- Upload more images into existing gallery
- Delete specific image from existing gallery (one at a time)
And roughly I am going to build this sample application in the following way:
- Use Rails 4.2.5 as the overall framework
- Use CarrierWave as the gem for handling multiple files uploading. And I am going to use the master branch, since according to the documentation, this feature is only enabled on the master branch
- Use PostgreSQL as the database, and particularly exploit the convenient
array
type supported by PostgreSQL for storing a sequence of "images" - Use Amazon S3 for storing the actual images uploaded
- Use Slim as the template engine and some HTML5 features, particular the <input> multiple attribute (eg.
<input type="file" name="imgs" multiple>
)
And now let’s build this application step by step.
Step 1 Setup the Rails application
First let’s run rails new sample-gallery-app-with-carrierwave
to generate a new Rails applicaiton which we are going to work on.
And inside file Gemfile
, I specify the Ruby version to 2.3.0
by adding ruby "2.3.0"
.
Next let’s remove gem sqlite3
, and add following gems
And now let’s configure config/database.yml
to use postgresql
instead of sqlite3
.
Finally, let’s run bundle
to get everything installed.
Step 2 Configure CarrierWave
Let’s create the initialization file config/initializers/carrier_wave.rb
and copy & paste in the example configuration from the official documentation.
You will need to replace sample credentials with yours.
And then Let’s generate our image uploader by calling rails generate uploader Image
, this should generate a new file at path app/uploaders/image_uploader.rb
.
For this newly generated uploader, let’s change the storage from from :file
to :fog
(change line storage :file
to storage :fog
).
Step 3 Scaffolding the basic
Now we have the our uploader ImageUploader
, let’s could go ahead scaffolding out our application.
Let’s Run rails g scaffold gallery title:string
, and add mount_uploaders :images, ImageUploader
into the Active Record class, so that our /app/models/gallery.rb
looks like
Then lets create the images related migration by calling:
rails g migration AddImagesToGallery
And in the generated migration file, let’s write
This is how we add a array column images
to our galleries table for holding images.
Next let’s run rake db:migrate
and rake db:setup
to get our database updated.
Step 4 Implement create gallery feature
Let’s edit app/views/galleries/_form.html.slim
file to add a multiple files field to the form.
Then let’s edit the #gallery_params
method of GalleriesController
to accept multiple images.
Next let’s edit the index
and show
views to display the images we uploaded.
Let’s add the following codes into app/views/galleries/index.html.slim
And also add the following codes into app/views/galleries/show.html.slim
Now let’s start our Rails application and hit http://localhost:3000/galleries/new
to create a gallery.
After creation you should see something like the following screenshot when visiting http://localhost:3000/galleries
.
Step 5 Implement add more images feature
In step 5 let’s modify our routes, and nest an images resources under galleries resources, so that our config/routes.rb
file should look like something below
And now let’s generate the images controllers by calling rails generate controller Images
.
Then let’s write the actual codes responsible for adding more images into existing gallery.
And as you could see in the codes, when we need to add more images we simply add more images into the array attribute , and save the gallery object afterwards.
Now let’s modify the show
view to take advantage of this ImagesController
by adding the following codes
Now we let’s visit show
page of a single individual gallery, and we should be able to use the form there to add more images to existing galleries.
Step 6 Implement remove single image feature
Now let’s implement the remove single image feature.
Firstly let’s update routes to
The let’s implement the actual #destory
action of ImagesController
, so that the controller should looks like this:
As shown in the codes, when we want to delete certain image, we first remove the image from the array, then delete the image from S3, and lastly re-assign the modified images array back to gallery object.
Now let’s modify our show
view to use this action.
Let’s modify the code block we used to display the images to
Now when you visit a gallery, you should see something like following screenshot
Congraulations!
Now you have completed the tutorial, and finish the three features we set to complete, you are good to go ahead refactoring those codes so that you could put them into productions.
You should move the add/remove related codes into two service classes, add some validations here and there, enhance the error messages, improve the front-end user experience, and make the codebase more robust, but they are out of the scope of this blog post, thus not covered here.
That’s all for this post.
Happy coding.
Till next time.