Upload ảnh bằng Carrierwave tới Amazon S3
1. Giới thiệu Tại một số thời điểm trong quá trình sử dụng ứng dụng Rails của bạn, bạn sẽ muốn người dùng (có thể là admin hoặc người dùng bình thường) upload hình ảnh của họ. Thông thường, bạn sẽ muốn thay đổi kích thước của hình ảnh đó thành một kích thước cụ thể nào đó (Mà không phải thay đổi ...
1. Giới thiệu
Tại một số thời điểm trong quá trình sử dụng ứng dụng Rails của bạn, bạn sẽ muốn người dùng (có thể là admin hoặc người dùng bình thường) upload hình ảnh của họ. Thông thường, bạn sẽ muốn thay đổi kích thước của hình ảnh đó thành một kích thước cụ thể nào đó (Mà không phải thay đổi kích thước bằng CSS!) và bạn có thể sẽ muốn có hình ảnh có kích thước khác nhau. Bạn có thể lưu chúng ở file hệ thống, nhưng có một giải pháp tốt hơn là upload chúng lên một dịch vụ lưu trữ tệp trực tuyến. Có rất nhiều dịch vụ lưu trữ tệp trực tuyến, các bạn có thể tìm hiểu qua google, ở bài viết này mình sẽ hướng dẫn các bạn tải ảnh lên dịch vụ S3 của Amazon bằng gem Carrierwave. Bắt đầu thôi nào!
2. Cài đặt
Mình sẽ tạo một dự án demo cho các bạn dễ hiểu hơn
- Tạo dự án mới:
rails new carrierwave_to_s3
- Chỉnh sửa Gemfile, thêm các dòng sau vào
gem "carrierwave" gem "mini_magick" gem "fog"
Mình sẽ nói qua về các gem này: Carrierwave là gem cho phép upload hình ảnh, MiniMagick cho phép sử dụng để thay đổi kích thước và cắt hình ảnh, và cuối cùng Fog cho phép upload hình ảnh lên S3 một cách dễ dàng. (Lưu ý: Fog thực sự mạnh mẽ và giúp bạn quản lý rất nhiều thứ trên Amazon Web Services (AWS), nhưng ở đây mình sẽ chỉ sử dụng nó để tải hình ảnh lên S3).
- Sau khi đã thêm các gem xong, bạn chạy lệnh để cài đặt chúng
bundle install
- Giờ chúng ta sẽ tạo model để lưu URL của những hình ảnh đã upload. Để đơn giản mình sẽ tạo model uploads với một trường là image_url. Chạy lệnh sau:
rails g migration create_uploads
- Chỉnh sửa trong file migration vừa được tạo ra và migrate database
class CreateUploads < ActiveRecord::Migration def change create_table :uploads do |t| t.string :image_url t.timestamps end end end
rails db:migrate
- Tiếp đến chúng ta sẽ tạo model "Upload". Tạo file upload.rb trong thư mục app/models với nội dung như sau
class Upload < ActiveRecord::Base mount_uploader :image_url, ImageUploader end
*mount_uploader sẽ liên kết trường image_url với URL của file chúng ta đã lưu trữ.
- Chúng ta đã gọi một class có tên ImageUploader, giờ chúng ta sẽ tạo nó. Chạy lệnh sau:
rails generate uploader Image
Lệnh này sẽ tạo một file tên image_uploader.rb trong thư mục app/uploaders, giờ sẽ chỉnh sửa file này đễ có thể upload và resize hình ảnh
class ImageUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick storage :fog def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end version :large do process resize_to_limit: [800, 800] end version :medium, from_version: :large do process resize_to_limit: [500, 500] end version :thumb, from_version: :medium do process resize_to_fit: [100, 100] end version :square do process resize_to_fill: [500, 500] end end
Chúng ta cần include MiniMagick (để resize hình ảnh với các kích thước khác nhau). Chúng ta cũng sử dụng Fog để lưu trữ (upload ảnh lên S3). Ngoài ra bạn có thể sử dụng file system để lưu trữ (storage :file), tuy nhiên cách này không được khuyến khích bởi vì nó không thể mở rộng (nếu ứng dụng Rails của bạn chạy trên nhiều máy) và nó không tối ưu (ứng dụng Rails có thể phải đưa lên một file hình ảnh lớn). Bạn có thể tùy chỉnh nhiều kích cỡ hình ảnh mà bạn muốn, ở đây mình chỉ tạo 4 kích cỡ cho hình ảnh của mình. Bạn có thể tìm hiểu thêm về resizing tại Carrierwave documentation.
- Chúng ta đã có migration, model, uploader, giờ chúng ta cần thay đổi routes file (config/routes.rb)
Rails.application.routes.draw do resources :uploads end
- Chúng ta cần tạo controller để CRUD (creating, reading, updating and deleting) cho uploads model
rails generate controller uploads
class UploadsController < ApplicationController before_action :set_upload, only: [:show, :edit, :update, :destroy] # GET /uploads def index @uploads = Upload.all end # GET /uploads/1 def show end # GET /uploads/new def new @upload = Upload.new end # GET /uploads/1/edit def edit end # POST /uploads def create @upload = Upload.new post_upload_params if @upload.save redirect_to @upload, notice: 'Upload was successfully created.' else render :new end end # PATCH/PUT /uploads/1 def update if @upload.update post_upload_params redirect_to @upload, notice: 'Upload attachment was successfully updated.' else render :edit end end # DELETE /uploads/1 def destroy @upload.destroy redirect_to uploads_url, notice: 'Upload was successfully destroyed.' end private # Use callbacks to share common setup or constraints between actions. def set_upload @upload = Upload.find_by id: params[:id] end # Never trust parameters from the scary internet, only allow the white list through. def post_upload_params params.require(:upload).permit :image_url end end
- Giờ chúng ta cần tạo các file views nữa là gần như hoàn tất.
Index (app/views/uploads/index.html.erb)
<% @uploads.each do |upload| %> <%= link_to upload do %> <img src="<%= upload.image_url.square %>"> <% end %> <% end %><br> <%= link_to 'New upload', new_upload_path %>
New (app/views/uploads/new.html.erb) ```
<%= form_for(@upload) do |f| %> <div> <%= f.label :image_url %>
<%= f.file_field :image_url %> </div> <div> <%= f.submit %> </div> <% end %>
<%= link_to 'Back', uploads_path %> ```
Show (app/views/uploads/show.html.erb) ``` <%= link_to 'Destroy', @upload, method: :delete, data: { confirm: 'Are you sure?' } %> <img src="<%= @upload.image_url.url %>" />
<img src="<%= @upload.image_url.large %>" />
<img src="<%= @upload.image_url.medium %>" />
<img src="<%= @upload.image_url.thumb %>" />
<img src="<%= @upload.image_url.square %>" />
``` @upload.image_url.url sẽ trả về URL cho hình ảnh gốc của chúng ta Nếu muốn gọi các phiên bản khác của hình ảnh đã upload, chúng ta sẽ sử dụng @upload.image_url.version_type (version_type là các version của hình ảnh mà chúng ta khai báo ở file ***image_uploader.rb*** trong thư mục ***app/uploaders*** phía trên. - Gần xong rồi, điều cuối cùng phải làm là thiết lập S3. Đến trang login [AWS](https://console.aws.amazon.com/console/home), login xong rồi click đến trang [S3](https://console.aws.amazon.com/s3/home). Tiếp theo tạo một bucket và nhớ tên của nó (Bạn sẽ cần tên bucket để config). Bạn cần nhận các AWS credentials của mình theo hướng dẫn như hình dưới
Click "Welcome to AWS" rồi chọn "My Security Credentials". Tiếp theo click vào "Access Keys (Access Key ID and Secret Access Key)" và chọn "Create New Access Key" để nhận Access Key ID và Secret Access Key (Hai key này bạn sẽ cần để config). Khi đã có bucket name, access key ID và secret access key, giờ là lúc tạo file config cho S3 (config/initializers/s3.rb)
CarrierWave.configure do |config| config.fog_credentials = { :provider => 'AWS', :aws_access_key_id => "YOUR AMAZON ACCESS KEY", :aws_secret_access_key => "YOUR AMAZON SECRET KEY", :region => 'us-west-1' # Change this for different AWS region. Default is 'us-east-1' } config.fog_directory = "bucket-name" end
Vậy là xong, giờ bạn có thể upload ảnh lên server S3 của Amazon rồi đấy. Cảm ơn các bạn đã xem bài viết của mình, lần đầu viết bài, có gì sai sót các bạn chỉ bảo giúp mình.