你必須很努力

Day18 - 匯入 excel-應用篇

2021/10/01
字數統計: 557閱讀時間: 2 min

前言

使用者除了有匯出報表的需求外,也會有需要大量匯入的情境,匯入會更需要驗證輸入的資料,有可能是空的資料、跟預期輸入 Excel 完全不同、重複的資料 (需看情境是否接受重複的資料)、部分資料輸入錯誤...等

實作

這邊會需要使用到 rooroo-xls 這 2 個 Gem ,可參考此 pr

1
2
3
4
# Gemfile

gem 'roo', '~> 2.8', '>= 2.8.3'
gem 'roo-xls', '~> 1.2'

以下範例有做簡單的驗證,像是判斷輸入的 Excel 標題列是否一樣、是否有資料、是否有重複的資料、是否與 DB 資料重複

大量匯入時,可改用 activerecord-import 處理,可參考這篇文章

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# app/services/shops_excel/parser.rb

module ShopsExcel
class ParseError < StandardError; end

class Parser
def execute(file_path)
list = read_excel(file_path)
sheet = list.sheet(0)
validate_title_names!(sheet)
validate_shop_names!(sheet)
process_shops(sheet)
rescue ParseError => e
subject = "[#{self.class} Error] #{e.message}, \nbacktrace: #{e.backtrace}"
Rails.logger.error subject
end

private

def read_excel(file_path)
Roo::Spreadsheet.open(file_path)
end

def validate_title_names!(sheet)
return if sheet.row(1) == ShopsExcel::Generator::TITLES

raise ParseError, '輸入資料有誤,比對 Excel 標頭與預期不同'
end

def validate_shop_names!(sheet)
all_shop_names = sheet.column(1)[1..-1]
raise ParseError, '無資料' if all_shop_names.blank?
raise ParseError, '有重複的商家名稱,請檢查' if all_shop_names.uniq.size != all_shop_names.size

existed_shop_names = Shop.where(name: all_shop_names).pluck(:name)
return if existed_shop_names.blank?

raise ParseError, "有 #{existed_shop_names.size} 筆已建立過: #{existed_shop_names.join(', ')}"
end

def process_shops(sheet)
shops = []
(2..sheet.last_row).each do |index|
col_values = sheet_row(sheet, index)
shop = Shop.new(col_values)
shops << shop
end
Shop.import(shops)
end

def sheet_row(sheet, index)
{
name: sheet.row(index)[0], # 商家名稱
email: sheet.row(index)[1], # 信箱
note: sheet.row(index)[2], # 備註
}
end
end
end

rails console 輸入以下

範例的 Excel 檔

1
2
3
4
# rails console

file = Rails.root.join("data/商家總表.xlsx").to_s
ShopsExcel::Parser.new.execute(file)

小結

匯入要做的基本驗證蠻多的,需考量各種情境,並盡可能地去預防處理,和匯出一樣,建議都是放 sidekiq 處理,完成後再寄信給使用者,告知匯入結果

參考資料

  1. Roo GitHub

鐵人賽文章連結:https://ithelp.ithome.com.tw/articles/10272591
medium 文章連結:https://link.medium.com/Teq2MzayRjb
本文同步發布於 小菜的 Blog https://riverye.com/

備註:之後文章修改更新,以個人部落格為主

原文連結:https://riverye.com/2021/10/01/Day18-匯入-excel-應用篇/

發表日期:2021-10-01

更新日期:2022-09-23

CATALOG
  1. 1. 前言
  2. 2. 實作
  3. 3. 小結
  4. 4. 參考資料