12/08/2018, 15:26

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

Hướng dẫn lấy key S3

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.
0