Cách sử dụng gem to_xls-rails trong rails
Bài đăng này đã không được cập nhật trong 9 năm
**1. Đặt vấn đề **
Có nhiều Gem trong rails để hỗ trợ việc export file excel như gem axlsx
và axlsx_rails
. Trong đấy có một cách đơn giản nhất và được dùng nhiều nhất là không cần dùng gem mà chỉ cần dùng file có định dạng .xls.erb
. Ví dụ như file views/customers/show.xls.erb
sau
<?xml version="1.0"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<Worksheet ss:Name="Sheet1">
<Table>
<Row>
<Cell><Data ss:Type="String">ID</Data></Cell>
<Cell><Data ss:Type="String">First name</Data></Cell>
<Cell><Data ss:Type="String">Last name</Data></Cell>
<Cell><Data ss:Type="String">Age</Data></Cell>
<Cell><Data ss:Type="String">Addess</Data></Cell>
<Cell><Data ss:Type="String">Tel</Data></Cell>
<Cell><Data ss:Type="String">Email</Data></Cell>
<Cell><Data ss:Type="String">Birth day</Data></Cell>
<Cell><Data ss:Type="String">Job</Data></Cell>
</Row>
<% @customers.each do |customer| %>
<Row>
<Cell><Data ss:Type="Number"><%= customer.id %></Data></Cell>
<Cell><Data ss:Type="String"><%= customer.first_name %></Data></Cell>
<Cell><Data ss:Type="String"><%= customer.last_name %></Data></Cell>
<Cell><Data ss:Type="String"><%= customer.age %></Data></Cell>
<Cell><Data ss:Type="Number"><%= customer.address %></Data></Cell>
<Cell><Data ss:Type="Number"><%= customer.tel %></Data></Cell>
<Cell><Data ss:Type="Number"><%= customer.email %></Data></Cell>
<Cell><Data ss:Type="Number"><%= customer.birth_day %></Data></Cell>
<Cell><Data ss:Type="Number"><%= customer.job %></Data></Cell>
</Row>
<% end %>
</Table>
</Worksheet>
</Workbook>
Nhưng liệu file bạn export ra theo cách này thì bạn không thể import file excel đươc vào mà phải chỉnh lại format về Use Microsoft Excel 97/2000/XP/2003 Format
của file excel. Mình có một cách để giải quyết vấn đề này bằng 1 ứng dụng export ra file excel và import chính file excel mà hệ thống của mình xuất ra có sử dụng gem
to_xls-rails
. Sau đây mình xin trình bày về cách thức xây dựng ứng dụng
**2. Chức năng export ra file excel **
Trước hết bạn cần cài gem to_xls-rails
Gemfile
gem 'to_xls-rails'
Sau khi cài xong, bạn cần cấu hình trong file RAILS_ROOT/config/initializers/mime_types.rb
Mime::Type.register_alias "text/excel", :xls
Tại views/customers/show.html.erb bạn cần tạo 1 đường link để export file excel và 1 đường link về trang edit
<h1>Export file excel</h1>
<table style="margin: 0 auto">
<tr>
<th>First name: </th>
<td><%= @customer.first_name %></td>
</tr>
<tr>
<th>Last name: </th>
<td><%= @customer.last_name %></td>
</tr>
<tr>
<th>Age: </th>
<td><%= @customer.age %></td>
</tr>
<tr>
<th>Tel: </th>
<td><%= @customer.tel %></td>
</tr>
<tr>
<th>Address: </th>
<td><%= @customer.address %></td>
</tr>
<tr>
<th>Email: </th>
<td><%= @customer.email %></td>
</tr>
<tr>
<th>Birth day: </th>
<td><%= @customer.birth_day %></td>
</tr>
<tr>
<th>Job: </th>
<td><%= @customer.job %></td>
</tr>
<tr>
<%= link_to [@customer, format: :xls] do %>
<%= button_tag "Export excel" %>
<% end %>
<%= link_to edit_customer_path(@customer) do %>
<%= button_tag "Edit" %>
<% end %>
</tr>
</table>
Khi bạn click vào đường link export excel thì chương trình sẽ gọi đến hàm show trong customers_controller.html.rb
. Nếu bạn xuất ra tất cả các danh sách customer thì bạn chỉ cần các câu lệnh đơn giản sau
def index
@customers = Customer.all
respond_to do |format|
format.html
format.xls{send_data @customers.to_xls}
end
end
Nhưng ở đây chúng ta chỉ cần export ra 1 @customer thì sẽ cần viết phức tạp hơn 1 chút
def show
@customer = Customer.find params[:id]
respond_to do |format|
format.html
format.xls do
send_data(
[Customer.new].to_xls(except: Customer::HEADERS_EXCEPT,
prepend: [Customer::HEADERS, Customer.perform(@customer)]),
type: "application/excel; charset=utf-8; header=present",
filename: "#{@customer.full_name} #{Time.now.strftime("%Y/%m/%d %H:%M:%S")}.xls"
)
end
end
prepend
là giá trị bạn sẽ xuất ra
type
là kiểu dữ liệu mà bạn muốn export ra
filename
bao gồm tên đối tượng @customer và thời gian hiện tại
Tại model customer.rb
class Customer < ActiveRecord::Base
HEADERS = %w(id first_name last_name age address tel
email birth_day job)
HEADERS_EXCEPT = HEADERS + %w(created_at updated_at)
def full_name
"#{first_name} #{last_name}"
end
class << self
def perform customer
HEADERS.map{|header| customer.send(header)}
end
end
Trong đấy HEADERS
để khai báo các trường của customer. Một nhược điểm của gem to_xls-rails
là khi export ra file excel thì dòng thứ hai của file excel sẽ mặc định là tên của các trường và viết hoa chữ cái đầu tiên. Như vậy sẽ gây bất tiện cho người dùng, đặc biệt khi người dùng muốn xuất ra dòng đầu tiên là tiếng nhật (bạn có thể tham khảo chức năng này ở bài viết I18n với human attibutes trên viblo: https://viblo.asia/phan.thanh.giang/posts/zNPVMa6QGQOk).
Chính vì vậy chúng ta phải thêm vào HEADERS_EXCEPT
gồm tất cả các trường trong HEADER thêm vào 2 trường created_at và updated_at để loại đi việc lặp trường.
3. Chức năng import vào file excel
Trước tiên, bạn cần cài thêm hai gem là roo
và roo-xls
, hai gem này hỗ trợ việc đọc rất nhiều định dạng file khác như csv,ods, xlsx, xls, xml...
Tại giao diện edit.html.erb
, ta có form để import file excel
<%= form_for @customer, multipart: true do |f| %>
<%= f.file_field :file, size: "70" %>
<%= f.submit "import" %>
<% end %>
customers_controller.rb
def edit
@customer = Customer.find params["id"]
end
def update
@customer = Customer.find params["id"]
@customer = Customer.import params["customer"]["file"], @customer
redirect_to customer_path
end
Bạn sẽ cần truyền vào hàm import file excel và đối tượng bạn muốn import vào là @customer. Biến @customer sẽ được gán lại cho chính nó sau khi gọi đến hàm import trong model
customer.rb
class << self
def import file, customer
return unless (spreadsheet = open_spreadsheet file)
header = spreadsheet.row 1
row = Hash[[header, spreadsheet.row(2)].transpose]
HEADERS.each do |field|
customer.send "#{field}=", row[field]
end
customer.save
end
def open_spreadsheet file
case File.extname File.basename file.path
when ".xls" then Roo::Excel.new file.path
when ".xlsx" then Roo::Excelx.new file.path
end
end
end
header
là hàng 1 của file excel
["id", "first_name", "last_name", "age", "address", "tel", "email", "birth_day", "job"]
row
chính là tập hợp các giá trị tương ứng với mỗi cột trong file excel.
{"id"=>1.0,
"first_name"=>"nghia",
"last_name"=>"doan dai",
"age"=>114.0,
"address"=>"Ha noi",
"tel"=>"01694068234",
"email"=>"nghiadoandai@gmail.com",
"birth_day"=>Fri, 01 Feb 1991,
"job"=>"IT"}
Câu lệnh customer.send "#{field}=", row[field]
sẽ lấy từng giá trị trong mỗi cột để gán cho các trường trong @customer và lưu ngay tại model hoặc bạn có thể chuyển biến @customer lên controller để lưu
4. Tài liệu tham khảo và Kết luận
Mình có đưa source lên github, các bạn có thể vào địa chỉ sau để tham khảo thêm : https://github.com/nghiabk/import_and_export_xls Ngoài ra, mình có tham khảo thêm về các gem ở trên các trang https://github.com/roo-rb/roo https://github.com/liangwenke/to_xls-rails Đây không phải là cách duy nhất trong rails để import file excel đã export. Bài viết của mình mới chỉ mới trình bày được 1 cách nên rất mong nhận được sự đóng góp ý kiến của các bạn về các cách làm khác. Mình xin cảm ơn!
All rights reserved