0

Upload Files to Database in Rails 4 Without Paperclip

I. Giới thiệu

Như đã biết thì gem Paperclip là một gem rất hữu dụng trong các app Rails nhưng nó không support cho việc save file vào trong database. Trong một số tình huống, truy cấp vòa filesystem hoặc dịch vụ bên ngoài như Amazon S3 thì không được khả thi. Hoặc đơn giản là muố đưa các files vào trong cơ sở dữ liệu.

II. Hướng dẫn cách làm

  • Đầu tiên, tạo một model để lưu thông tin file.
- filename:string
- content_type:string
- fine_contents:binary
  • Note: để tạo nhanh 1 model như trên ta có thể sử dụng: rails g scaffold document filename:string content_type:string file_contents:binary

  • Dưới đây là: code for model và file migration

# app/models/document.rb
class Document < ActiveRecord::Base
end

# db/migrate/20140602005921_create_documents.rb
class CreateDocuments < ActiveRecord::Migration
  def change
    create_table :documents do |t|
      t.string :filename
      t.string :content_type
      t.binary :file_contents

      t.timestamps
    end
  end
end
  • Tiếp theo chúng ta sẽ thêm file input cho model's form
<!-- app/views/documents/_form.html.erb -->
<%= form_for(@document) do |f| %>
  <div class="field">
    <%= f.file_field :file %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
  • Tiếp đến, chúng ta cần đảm bảo rằng phải permit params[:file], bởi thế chúng ta mới sử dụng được file trong model
# app/controllers/documents_controller.rb
def document_params
  params.require(:document).permit(:file)
end
  • Cuối cùng update cho model có thể đọc và lưu file lại. Ở trong Rails, thì file input được passed như là một UploadedFile, có thể coi nó như một normal file. Chúng ta sẽ viết một hàm initialize trong model:
# app/models/document.rb
def initialize(params = {})
  file = params.delete(:file)
  super
  if file
    self.filename = sanitize_filename(file.original_filename)
    self.content_type = file.content_type
    self.file_contents = file.read
  end
end
private
  def sanitize_filename(filename)
    # Get only the filename, not the whole path (for IE)
    # Thanks to this article I just found for the tip: http://mattberther.com/2007/10/19/uploading-files-to-a-database-using-rails
    return File.basename(filename)
  end
  • Bây giờ chúng ta có một app mà ở đó users có thể upload files lên, nhưng vẫn chưa thể download được. Chúng ta có thể tao ra show action trong documents_controller để download file. Để làm như vậy chỉ cận gửi các file đã lưu trong DB.
def show
  send_data(@document.file_contents,
            type: @document.content_type,
            filename: @document.filename)
end
  • Okey. Giờ chúng ta đã có 1 app Rails có thể upload và download file 1 cách rất đơn giản. Chúng ta có thể viết validate cho việc upload file, ví dụ như MB size, name ...
# app/models/document.rb

validate :file_size_under_one_mb

def initialize(params = {})
  # File is now an instance variable so it can be
  # accessed in the validation.
  @file = params.delete(:file)
  super
  if @file
    self.filename = sanitize_filename(@file.original_filename)
    self.content_type = @file.content_type
    self.file_contents = @file.read
  end
end

NUM_BYTES_IN_MEGABYTE = 1048576
def file_size_under_one_mb
  if (@file.size.to_f / NUM_BYTES_IN_MEGABYTE) > 1
    errors.add(:file, 'File size cannot be over one megabyte.')
  end
end
  • Vừa rồi chúng ta vừa tạo ra một application Rails có thể upload và download file.
  • Link APP DEMO
  • Source GITHUB

III. Tài liệu tham khảo

http://ryan.endacott.me/2014/06/10/rails-file-upload.html


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí