Advertisement
  1. Code
  2. Ruby on Rails

Upload với Rails và Paperclip

Scroll to top
Read Time: 12 min

() translation by (you can also view the original English article)

Đây là bài cuối cùng trong loạt bài "Uploading với Rails". Trong một vài tháng trước, chúng ta đã thảo luận về Shrine, Dragonfly và Carrierwave gem. Hôm nay, chúng ta sẽ bàn tới Paperclip của công ty Thoughtbot, nơi đang quản lý các gem như FactoryGirl và Bourbon.

Paperclip có lẽ là giải pháp quản lý kèm theo phổ biến nhất cho Rails (hơn 13 triệu lượt download), và có nguyên do rõ ràng cho điều này: nó có nhiều tính năng, một cộng đồng tuyệt vời và hệ thống tài liệu dễ hiểu. Vì vậy hi vọng bạn sẽ thấy hào hứng khi tìm hiểu về gem này!

Trong bài viết này bạn sẽ tìm hiểu làm thế nào để:

  • Chuẩn bị cho việc cài đặt Paperclip
  • Tích hợp Paperclip vào một ứng dụng Rails
  • Thêm phê chuẩn đính kèm
  • Tạo ảnh nhỏ (thumbnail) và xử lý hình ảnh
  • Ẩn đường dẫn (URL)
  • Lưu trữ file đính kèm trên Amazon S3
  • Bảo vệ an toàn file trên đám mây bằng cách đưa ra các logic cấp phép

Mã nguồn cho bài viết này có sẵn trên GitHub.

Chuẩn bị

Trước khi chúng ta tìm hiểu sâu vào phần mã nguồn, trước tiên hãy thảo luận về một số điều cần lưu ý mà bạn cần biết để có thể làm việc với Paperclip thành công:

  • Phiên bản mới nhất của Paperclip hỗ trợ Rails 4.2+ và Ruby 2.1+. Gem này cũng có thể sử dụng mà không cần Rails.
  • ImageMagick phải được cài đặt trên máy tính của bạn (nó có sẵn cho tất cả các nền tảng lớn), và Paperclip có thể truy cập vào nó.
  • Lệnh file có sẵn từ dòng lệnh. Đối với hệ điều hành Windows, phần này có sẵn thông qua Development Kit cho các nhà phát triển, vì vậy hãy làm theo những hướng dẫn này nếu bạn chưa cài đặt DevKit.

Khi bạn đã sẵn sàng, hãy tiếp tục và tạo một ứng dụng Rails mới (Tôi sẽ sử dụng Rails 5.0.2) không sử dụng bộ thử nghiệm mặc định:

1
rails new UploadingWithPaperclip -T

Tích hợp Paperclip

Đưa vào gem Paperclip:

Gemfile

1
gem "paperclip", "~> 5.1"

Cài đặt nó:

1
bundle install

Giả sử chúng ta đang tạo một ứng dụng quản lý sách trong đó có một danh sách các cuốn sách. Mỗi cuốn sách có một tiêu đề, một phần mô tả và tên tác giả, cùng với một hình ảnh bìa. Để bắt đầu, tổng hợp và áp dụng sự di trú sau:

1
rails g model Book title:string description:text image:attachment author:string
2
rails db:migrate

Lưu ý loại attachment được trình bày cho chúng tabằng Paperclip. Nó sẽ tạo ra bốn trường cho chúng ta:

  • tên_file_ảnh
  • kích_thước_file_ảnh
  • loại_nội_dung_ảnh
  • thời_gian_cập_nhật_ảnh

Trái ngược với các gem Shrine và Carrierwave, Paperclip không có một tập tin riêng biệt với các cấu hình. Tất cả cài đặt được xác định bên trong các mô hình bằng cách sử dụng phương pháp has_attached_file, vì vậy ta hãy thêm nó ngay bây giờ:

models/book.rb

1
has_attached_file :image

Trước khi tiếp tục tới phần chính, chúng ta hãy tạo ra một bộ điều khiển cùng với một số view và route.

Tạo Controller, Views, and Routes

Phần điều khiển của chúng ta rất cơ bản:

books_controller.rb

1
class BooksController < ApplicationController
2
  before_action :set_book, only: [:show, :download]
3
4
  def index
5
    @books = Book.order('created_at DESC')
6
  end
7
8
  def new
9
    @book = Book.new
10
  end
11
12
  def show
13
  end
14
15
  def create
16
    @book = Book.new(book_params)
17
    if @book.save
18
      redirect_to books_path
19
    else
20
      render :new
21
    end
22
  end
23
24
  private
25
26
  def book_params
27
    params.require(:book).permit(:title, :description, :image, :author)
28
  end
29
30
  def set_book
31
    @book = Book.find(params[:id])
32
  end
33
end

Đây là một index view và một partial:

views/books/index.html.erb

1
<h1>Bookshelf</h1>
2
3
<%= link_to 'Add book', new_book_path %>
4
<ul>
5
  <%= render @books %>
6
</ul>

views/books/_book.html.erb

1
<li>
2
  <strong><%= link_to book.title, book_path(book) %></strong> by <%= book.author %>
3
</li>

Bây giờ tới các route:

config/routes.rb

1
Rails.application.routes.draw do
2
  resources :books
3
  root to: 'books#index'
4
end

Rất tốt! Bây giờ hãy xử lý tới phần chính và tạo mã action mới và một biểu mẫu.

Tải lên tập tin

Nhìn chung, tải tập tin với Paperclip rất dễ. Bạn chỉ cần cho phép thuộc tính tương ứng (trong trường hợp này, chính là thuộc tính image và chúng ta đã cho phép nó) và đưa ra một trường tập tin trong biểu mẫu của bạn. Hãy cũng làm điều này bây giờ:

views/books/new.html.erb

1
<h1>Add book</h1>
2
3
<%= render 'form', book: @book %>

views/books/_form.html.erb

1
<%= form_for book do |f| %>
2
  <div>
3
    <%= f.label :title %>
4
    <%= f.text_field :title %>
5
  </div>
6
7
  <div>
8
    <%= f.label :author %>
9
    <%= f.text_field :author %>
10
  </div>
11
12
  <div>
13
    <%= f.label :description %>
14
    <%= f.text_area :description %>
15
  </div>
16
17
  <div>
18
    <%= f.label :image %>
19
    <%= f.file_field :image %>
20
  </div>
21
22
  <%= f.submit %>
23
<% end %>

Với thiết lập này, bạn đã có thể bắt đầu thực hiện các nội dung tải lên, nhưng đó là một ý tưởng tốt để đưa ra một số xác nhận nữa.

Thêm xác nhận (Validations)

Validations trong Paperclip có thể được viết bằng cách sử dụng các helper cũ như validates_attachment_presencevalidates_attachment_content_type hoặc bằng cách sử dụng phương pháp validates_attachment để xác định nhiều quy tắc cùng một lúc. Chúng ta hãy sử dụng lựa chọn cuối:

models/book.rb

1
  validates_attachment :image,
2
                       content_type: { content_type: /\Aimage\/.*\z/ },
3
                       size: { less_than: 1.megabyte }

Như bạn có thể thấy thì phần code trên đây khá đơn giản. Chúng ta yêu cầu các tập tin phải là một hình ảnh có kích thước nhỏ hơn 1 megabyte. Lưu ý rằng nếu xác nhận thất bại, không có phần xử lý tiếp theo nào sẽ được thực hiện. Paperclip đã có một số tin nhắn báo lỗi được cài đặt cho ngôn ngữ tiếng Anh, nhưng nếu bạn muốn hỗ trợ các ngôn ngữ khác, hãy thêm gem paperclip-i18n vào phần Gemfile của bạn.

Một điều quan trọng cần đề cập đến là Paperclip yêu cầu bạn xác nhận loại nội dung hoặc tên tập tin của tất cả phần đính kèm, nếu không nó sẽ đưa ra lỗi. Nếu bạn 100% chắc chắn rằng bạn không cần một xác nhận (validations) như vậy (hiếm khi xảy ra), hãy sử dụng do_not_validate_attachment_file_type để thể hiện một cách rõ ràng các trường không cần phải kiểm tra.

Sau khi thêm validations, hãy cài đặt hiển thị thông báo lỗi trong mẫu của chúng ta:

views/shared/_errors.html.erb

1
<% if object.errors.any? %>
2
  <h3>Some errors were found:</h3>
3
  <ul>
4
    <% object.errors.full_messages.each do |message| %>
5
      <li><%= message %></li>
6
    <% end %>
7
  </ul>
8
<% end %>

views/books/_form.html.erb

1
<%= render 'shared/errors', object: book %>

Hiển thị hình ảnh

Được rồi, vậy bây giờ hình ảnh được tải lên sẽ được hiển thị bằng cách nào đó. Điều này được thực hiện bằng cách sử dụng helper image_tag và một phương pháp url. Tạo một view hiển thị:

views/books/show.html.erb

1
<h1><%= @book.title %> by <%= @book.author %></h1>
2
3
<%= image_tag(@book.image.url) if @book.image.exists? %>
4
5
<p><%= @book.description %></p>

Chúng ta đang hiển thị một hình ảnh nếu nó thực sự tồn tại trên ổ đĩa. Hơn nữa, nếu bạn đang sử dụng lưu trữ đám mây, thì Paperclip sẽ thực hiện một yêu cầu kết nối mạng và kiểm tra sự tồn tại của tập tin. Tất nhiên, thao tác này có thể mất hời gian, vì vậy bạn có thể sử dụng present? hoặc file? như là các phương pháp thay thế: đơn giản là chúng sẽ đảm bảo rằng các trường image_file_name là thích ứng với một số nội dung.

Ẩn đường dẫn (URL)

Theo mặc định, tất cả các tập tin đính kèm được lưu trữ bên trong các thư mục public/system, do đó, có thể bạn sẽ muốn loại bỏ nó từ hệ thống điều khiển phiên bản:

.gitignore

1
public/system

Tuy nhiên, hiển thị một đường dẫn (URL) đầy đủ cho các tập tin không phải lúc nào cũng là một ý tưởng tốt, và bạn có thể cần phải ẩn nó đi bằng cách nào đó. Cách dễ nhất để cho phép ẩn đi là cung cấp hai tham số đối với has_attached_file method:

models/book.rb

1
url: "/system/:hash.:extension",
2
hash_secret: "longSecretString"

Các giá trị thích hợp sẽ được nội suy vào url tự động. hash_secret là một trường cần thiết, và là cách dễ nhất để tạo ra nó là bằng cách sử dụng:

1
rails secret

Làm việc với các style

Trong nhiều trường hợp, hình ảnh thủ nhỏ thường được hiển thị với một số định dạng trước về chiều rộng và chiều cao để tiết kiệm băng thông. Paperclip giải quyết điều này bằng cách sử dụng các styles: mỗi style có một tên và một loạt các quy tắc, như kích thước, định dạng, chất lượng, vv.

Giả sử rằng chúng tôi muốn hình ảnh ban đầu và hình thu nhỏ của nó được chuyển đổi sang định dạng JPEG. Hình thu nhỏ nên được cắt thành kích thước 300x300px:

models/book.rb

1
  has_attached_file :image,
2
                    styles: {
3
                        thumb: ["300x300#", :jpeg],
4
                        original: [:jpeg]
5
                    }

# là một thiết lập hình học có nghĩa: "cắt bớt nếu cần thiết trong khi duy trì tỷ lệ các chiều."

Chúng ta cũng có thể cung cấp các tùy chọn chuyển đổi bổ sung cho từng style. Ví dụ, chúng ta hãy đưa ra 70% chất lượng cho hình thu nhỏ trong khi loại bỏ tất cả phần siêu dữ liệu và 90% cho chất lượng hình ảnh ban đầu để làm cho nó nhỏ hơn một chút:

models/book.rb

1
  has_attached_file :image,
2
                    styles: {
3
                        thumb: ["300x300#", :jpeg],
4
                        original: [:jpeg]
5
                    },
6
                    convert_options: {
7
                        thumb: "-quality 70 -strip",
8
                        original: "-quality 90"
9
                    }

Tốt rồi! Hiển thị hình thu nhỏ và cung cấp các liên kết đến hình ảnh ban đầu:

views/books/show.html.erb

1
<%= link_to(image_tag(@book.image.url(:thumb)), @book.image.url, target: '_blank') if @book.image.exists? %>

Lưu ý rằng không giống như Carrierwave, Paperclip không cho phép bạn viết @book.image.thumb.url.

Nếu vì một số lý do nào đó mà bạn muốn cập nhật một cách thủ công hình ảnh được tải lên, thì bạn có thể sử dụng các lệnh sau đây để làm mới hình thu nhỏ, thêm style bị thiếu hoặc làm mới tất cả hình ảnh:

  • rake paperclip:refresh:thumbnails CLASS=Book
  • rake paperclip:refresh:missing_styles CLASS=Book
  • rake paperclip:refresh CLASS=Book

Các tập tin lưu trữ trên đám mây

Giống như tất cả các giải pháp tương tự, Paperclip cho phép bạn tải tập tin lên đám mây. Ngoài ra, nó còn hỗ trợ cho các bộ điều hợp mạng S3 và Fog và có cả các gem của bên thứ ba cho Azure và Dropbox. Trong phần này, tôi sẽ chỉ cho bạn cách để tích hợp Paperclip với Amazon S3. Đầu tiên, đưa vào gem aws-sdk:

1
gem 'aws-sdk'

Cài đặt nó:

1
bundle install

Tiếp theo, cung cấp một bộ lựa chọn mới cho phương pháp has_attached_file:

models/book.rb

1
  has_attached_file :image,
2
                    styles: {
3
                        thumb: ["300x300#", :jpeg],
4
                        original: [:jpeg]
5
                    },
6
                    convert_options: {
7
                        thumb: "-quality 70 -strip",
8
                        original: "-quality 90"
9
                    },
10
                    storage: :s3,
11
                    s3_credentials: {
12
                        access_key_id: ENV["S3_KEY"],
13
                        secret_access_key: ENV["S3_SECRET"],
14
                        bucket: ENV["S3_BUCKET"]
15
                    },
16
                    s3_region: ENV["S3_REGION"]

Ở đây, tôi là sử dụng gem dotenv vịn để thiết lập các biến môi trường. Bạn có thể cung cấp tất cả các giá trị trực tiếp bên trong các model, nhưng không làm cho nó có sẵn một cách công khai.

Điều thú vị là s3_credentials cũng chấp nhận một đường dẫn đến tập tin YAML chứa các keys của bạn và một tên bucket. Hơn nữa, bạn có thể thiết lập các giá trị khác nhau cho các môi trường khác nhau như thế này:

1
development:
2
  access_key_id: key1
3
  secret_access_key: secret1
4
production:
5
  access_key_id: key2
6
  secret_access_key: secret2

Chính là nó! Tất cả các tệp bạn tải lên bây giờ sẽ nằm trong bucket S3 của bạn.

Bảo vệ các tập tin trong đám mây

Giả sử bạn không muốn tập tin được tải lên của bạn có sẵn cho tất cả mọi người. Theo mặc định, tất cả các nội dung tải lên vào các đám mây được đánh dấu công khai, có nghĩa là bất cứ ai cũng có thể mở các tập tin thông qua liên kết trực tiếp. Nếu bạn muốn giới thiệu một số phép logic và kiểm tra xem những ai có thể xem các tập tin, thiết lập tùy chọn s3_permissions thành :private như thế này:

1
  has_attached_file :image,
2
                    styles: {
3
                        thumb: ["300x300#", :jpeg],
4
                        original: [:jpeg]
5
                    },
6
                    convert_options: {
7
                        thumb: "-quality 70 -strip",
8
                        original: "-quality 90"
9
                    },
10
                    storage: :s3,
11
                    s3_credentials: {
12
                        access_key_id: ENV["S3_KEY"],
13
                        secret_access_key: ENV["S3_SECRET"],
14
                        bucket: ENV["S3_BUCKET"]
15
                    },
16
                    s3_region: ENV["S3_REGION"],
17
                    s3_permissions: :private

Bây giờ, ngoại trừ bạn thì không ai sẽ có thể xem các tập tin. Do đó, hãy tạo một hành động mới download cho BooksController:

books_controller.rb

1
  def download
2
    redirect_to @book.image.expiring_url
3
  end

Hành động này chỉ đơn giản là sẽ chuyển hướng người dùng đến hình ảnh thông qua một liên kết hết hạn. Sử dụng cách tiếp cận này, bây giờ bạn có thể giới thiệu bất kỳ logic cho phép sử dụng các gem như như CanCanCan hay Pundit.

Đừng quên để thiết lập route phụ:

config/routes.rb

1
  resources :books do
2
    member do
3
      get 'download'
4
    end
5
  end

Phần trợ giúp nên được sử dụng như thế này:

1
link_to('View image', download_book_path(@book), target: '_blank')

Kết luận

Chúng tôi đã đến phần cuối cùng của bài viết này! Hôm nay chúng tôi đã tìm hiểu về Paperclip, một giải pháp quản lý tập tin đính kèm cho Rails, với các ví dụ thực tế và thảo luận về các khái niệm chính. Vẫn còn nhiều điều về gem này, vì vậy hãy xem thêm tài liệu hướng dẫn.

Ngoài ra, tôi khuyên bạn nên ghé thăm trang wiki của Paperclip vì nó có một danh sách các mục hướng dẫn "làm thế nào" và một loạt các liên kết đến các gem của bên thứ ba hỗ trợ Azure và Cloudinary và cho phép bạn dễ dàng tối thiểu hóa các tập tin được tải lên.

Cảm ơn vì đã đồng hành cùng với tôi, và hẹn gặp lại bạn!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.