Upload files trong Rails

Xin chào các bạn, hôm nay mình làm mẫu chi tiết cách upload một file hình ảnh trong rails, và bằng cách làm tương tự như mình làm sau đây, các bạn hoàn toàn có thể upload bất kì loại file nào.

Bắt đầu

Đầu tiên, bạn cần cài đặt gem attach, bằng cách thêm vào Gemfile, và sau đó chạy bundle install:

gem 'attach', '~> 1.0.1'

$ bundle install

Giống như việc lưu trữ file trong cơ sở dữ liệu, chúng ta cần phải tạo các migration cho các bảng để lưu trữ nội dung file. Trước khi run migration db, bạn cần rake attach:install:migrations để tự động thêm các migration cần thiết cho db của bạn.

$ rake attach:install:migrations

$ rake db:migrate

Config model

Thêm danh sách các file bạn muốn thêm vào model:

class Post < ActiveRecord::Base
  attachment :cover_photo
end

Uploading ảnh

Chúng sẽ có reader và writer cho tệp đính kèm mà mình muốn được tải lên. Ví dụ:

post = Post.new

# Đặt ảnh từ dữ liệu chúng ta có
post.cover_photo = some_binary_data
post.cover_photo.file_name = "cover-photo.jpg"
post.cover_photo.file_type = "image/jpg"

# Hoặc chúng ta dùng ActionDispatch::Http::UploadedFile
post.cover_photo = params[:person][:cover_photo]

# Hoặc có thể dùng đối tượng pre-constructed `Attach::File`
file = Attach::File.new(binary)
file.name = "cover-photo.jpg"
file.type = "image/jpg"
post.cover_photo = file

Cần chú ý rằng khi chúng ta gọi reader sẽ luôn trả về đối tượng Attach::Attachment bất kể bạn đưa vào dữ liệu gì. Nếu chúng ta đưa vào một tệp đã được tải lên thì dữ liệu đó sẽ được chuyển sang đối tượng Attach::Attachment tại điểm mà dữ liệu được đặt.

Uploading từ form

Tiếp theo, cần cho phép tải các tệp lên server bằng cách thêm các trường cho form post, trước hết ta thêm một file_field:

<% form_for @post do |f| %>
  <!--- Your existing form fields -->
  <%= f.label :cover_photo %>
  <%= f.file_field :cover_photo %>
  <!--- A submit button -->
<% end %>

Để đảm bảo tham số cover_photo được cho phép sử dụng trong controller, ta cần thêm cover_photo attribute vào trong dòng params.require(:post).permit(...), ví dụ như sau:

params.require(:post).permit(:title, :content, :cover_photo)

Truy cập file đã upload

Có rất nhiều cách để truy cập vào các file đã tải lên trên server, đơn giản nhất là buil-in middleware để chứa các tệp đính kèm. Giả sử ta đang cần upload ảnh bìa cho một bài post, và giờ cần show ảnh bìa đó. Mở file view của action show và chèn thẻ image như sau:

<% if @post.cover_photo %>
  <%= image_tag @post.cover_photo.url %>
<% end %>

Phương thức url sẽ trả về một đường dẫn, mà sau đó middleware sẽ chặn và hiển thị tệp mà bạn đã chọn. Mặc định nó sẽ có dạng /attachment/736a33aa-e1b2-453e-808c-5a668e6e2fa3/photo.jpg. Nếu bạn tải lên một file không phải hình ảnh, bạn có thể muốn liên kết đến tệp này thay vì hiển thị chúng lên trình duyệt:

<%= link_to "Download product datasheet", @product.datasheet.url %>

Thêm một số validation cơ bản

Hiện tại, bạn có thể tải lên bật kì một file nào, tuy nhiên ta cần thêm một số validation để đảm bảo tệp tải lên trông giống hình ảnh. Mở Post model và thêm các thông tin sau:

class Post < ActiveRecord::Base
  attachment :cover_photo do
    validator do |attachment, errors|
      unless attachment.file_type =~ /\Aimage\/(jpeg|png|gif)/
        errors << "must have an image content type"
      end
    end
  end
end

Xóa tệp đính kèm

Có 2 cách để có thể xóa tệp đính kèm, đơn giản nhất là gọi phương thức destroy.

def delete_cover_photo
  post = Post.find(params[:id]
  post.cover_photo.destroy
end

Ngoài ra, bạn có thể sử dụng checkbox trên form của mình để gửi yêu cầu xóa tệp đính kèm. Để thực hiện điều này, chúng ta cần thêm một checkbox vào form cho thích hợp:

<% if f.object.cover_photo %>
  <%= f.check_box :cover_photo_delete %>
  <%= f.label :cover_photo_delete, "Delete cover photo?" %>
<% end %>

Bạn cũng cần thêm cover_photo_delete vào danh sách các tham số được phép trong controller của bạn.

params.require(:post).permit(:title, :content, :cover_photo_delete)

Giờ hãy thử mở form và xóa ảnh của một bài đăng bạn đã lưu để thử kiểm tra nhé!

Finish

Vậy là qua bài viết, chúng ta đã biết thêm một cách upload hình ảnh (có thể dùng cho các loại tệp khác không phải hình ảnh) đơn giản trong rails. Cảm ơn các bạn đã quan tâm bài viết ! Xem thêm:

Follow the next tutorial about resizing images

Read more about the Attach gem

Tài liệu tham khảo

https://atech.blog/atech/file-attachments-in-rails-tutorial