Import dữ liệu từ các định dạng csv và xlsx, export dữ liệu ra định dạng pdf và xls trong Rails
Bài đăng này đã không được cập nhật trong 5 năm
Giới thiệu vấn đề
- Đối với các hệ thống web lớn, lưu trữ nhiều loại dữ liệu thì việc import một tập tin với lượng dữ liệu lớn lên hệ thống là một nhu cầu tất yếu. Thường các tập tin cần import thường ở định dạng csv, office (word, excel, ...). Trong Rails, để xử lý vấn đề import dữ liệu nó đã cung cấp sẵn công cụ import dữ liệu từ file csv mà lập trình viên có thể dễ dàng sử dụng
- Ngoài ra, ở các hệ thống có nhu cầu quản lý, tổng kết và trích xuất dữ liệu thì hệ thống cần cung cấp tính năng export dữ liệu ra các định dạng phổ biến
Ở bài viết này, mình xin trình bày phương pháp import dữ liệu và export dữ liệu ra một số định dạng quen thuộc như pdf, xls, xlsx
Dự án demo
Để minh họa cho các kĩ thuật mà mình nhắc đến ở trên, ta sẽ tạo một project Rails để áp dụng
Tạo project
rails new demo_export_import
Tạo một số model với đầy đủ các thành phần controller, views, model với câu lệnh
rails generate scaffold User name:string email:string
Sau đây, mình xin vào phần chính của bài viết. Giao diện của sản phẩm như sau
Import dữ liệu từ tập tin csv, xls, xlsx
Để import dữ liệu từ các tập tin trên, mình sử dụng gem "roo" và để tăng hiệu suất import dữ liệu vào database sử dụng gem "activerocord-import"
gem "roo"
gem "activerecord-import"
Định nghĩa route và tạo thẻ input nhận file cần import
<%= form_tag import_users_path, multipart: true do %>
<%= file_field_tag :file %>
<%= submit_tag "Import", class: "btn btn-success" %>
<% end %>
Trong model User, định nghĩa các phương thức dưới đây để thực hiện import dữ liệu
def import_file file
# Mở file được xử lý
spreadsheet = open_spreadsheet(file)
# Gán header (tên các cột) là hàng 1 trong file
header = spreadsheet.row(1)
# Khởi tạo một mảng rỗng để chứa các bản ghi
rows = []
# Đọc bản ghi dữ liệu từ dòng thứ 2 đến dòng cuối và gán vào mảng trên
(2..spreadsheet.last_row).each do |i|
rows << spreadsheet.row(i)
end
# Sử dụng phương thức import mà thư viện activerecord-import cung cấp để tạo dữ liệu đạt hiệu suất cao
import! header, rows
end
# Phương thức mở tập tin để import tùy vào từng loại file
def open_spreadsheet(file)
case File.extname(file.original_filename)
when ".csv" then Roo::CSV.new(file.path)
when ".xls" then Roo::Excel.new(file.path)
when ".xlsx" then Roo::Excelx.new(file.path)
else raise "Unknown file type: #{file.original_filename}"
end
end
Trong controller, tạo phương thức import để xử lý
def import
if params[:file].present?
User.import_file params[:file]
redirect_to root_url
flash[:success] = "Data imported"
else
redirect_to root_url
flash[:error] = "Data not imported"
end
end
Export dữ liệu ra định dạng pdf
Để export dữ liệu ra định dạng pdf, ta thêm 2 gem sau vào Gemfile và chạy bundle để cài đặt chúng
gem "wicked_pdf"
gem "wkhtmltopdf-binary"
Tạo template chung cho giao diện của tập tin pdf được tạo ra bằng cách tạo các tập tin pdf.js
và pdf.scss
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<%= wicked_pdf_stylesheet_link_tag 'pdf' %>
<%= wicked_pdf_javascript_include_tag 'jquery', 'users', 'pdf' %>
</head>
# Đánh số trang khi export file
<body onload="number_pages()">
<%= yield %>
</body>
</html>
Đánh số trang khi export file
function number_pages() {
var vars={};
var x=document.location.search.substring(1).split('&');
for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = decodeURIComponent(z[1]);}
var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
for(var i in x) {
var y = document.getElementsByClassName(x[i]);
for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
}
}
Tạo nút bấm để thực hiện export dữ liệu ra tập tin pdf
<%= link_to export_users_path(format: :pdf)do %>
<i class="fa fa-paperclip" aria-hidden="true"></i> PDF Attachment
<% end %>
Phương thức export ra pdf file với các tùy chọn mà gem "wkhtmltopdf-binary"
cung cấp
def export
@users = User.order created_at: :desc # Xác định nội dung file
respond_to do |format|
format.pdf do
render pdf: 'users', # Xác định view truyền nội dung file
layout: 'pdf.html', # Xác định layout mà file sẽ nhận
template: 'users/export', # Xác định layout mà file sẽ nhận
footer: {right: '[page]'}, # Thêm số trang ở bên phải phần footer
margin: { :top => 15, :bottom => 21, :left => 12, :right => 12 }, # Căn lề cho file
orientation: 'Landscape', page_size: 'A4' # Xác định cỡ giấy và chiều hiển thị bảng
end
end
end
Tạo tập tin users.pdf.html
để tạo nội dung cho tập tin (đổ dữ liệu vào file)
<div class="container">
<h1 align="center">List Users</h1>
<table class='table table-bordered'>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Create at</th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.email %></td>
<td><%= l user.created_at, format: :time %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
Export dữ liệu ra định dạng xls, xlsx
Để export dữ liệu ra định dạng excel, ta thêm 2 gem sau vào Gemfile và chạy bundle để cài đặt chúng
gem "axlsx"
gem "axlsx_rails"
Tạo nút bấm để thực hiện export dữ liệu ra tập tin xls, xlsx
<%= link_to export_users_path(format: :xlsx)do %>
<i class="fa fa-file-excel-o" aria-hidden="true"></i> Download as Excel
<% end %>
Tạo tập tin export.xlsx.axlsx
để tạo nội dung cho tập tin (đổ dữ liệu vào file)
# Tạo một file workbook mới
wb = xlsx_package.workbook
# Tạo worksheet có tên là Users
wb.add_worksheet(name: "Users") do |sheet|
# Thêm nội dung hàng tiêu để cho file
sheet.add_row %w(name email)
# Tạo các bản ghi dữ liệu cho file
@users.each do |user|
sheet.add_row [user.name, user.email]
end
end
Phương thức export ra xlsx file
def export
@users = User.order created_at: :desc # Xác định nội dung file
respond_to do |format|
format.xlsx
end
end
Sản phẩm khi chạy sẽ cho kết quả như sau
Bài viết có tham khảo một số bài sau
https://viblo.asia/p/importing-records-from-csv-and-excel-in-rails-5-ByEZk0QqlQ0
https://github.com/roo-rb/roo
https://github.com/mileszs/wicked_pdf
https://github.com/randym/axlsx
All rights reserved