xls無痛升級到xlsx
我們的專案中大量使用了 xls.erb 模版來生成 excel 報表, 在我前面寫的文章ofollow,noindex" target="_blank">Rails Respond Format 應用 中,我介紹過如何使用 xls.erb 模版來生成 excel 報表以及 xls.erb 模版的方便之處. 現在問題來了, xls.erb 模版生成的 excel 報表是 xls 型別的, 最新的 MS office excel 以及一些其它平臺的 office excel 軟體打不開 xls 型別的檔案, 所以我們需要將 xls 型別的報表升級到 xlsx 型別的報表. 為了讓升級的過程不那麼痛苦,我們 需要大量複用以前的 xls.erb 模版, 升級的過程大致如下:
xls.erb 模版例子
xls.erb 模版其實就是一種用於生成 xml 資料的模版,和 html.erb 模版是一樣的邏輯, 只不過 html.erb 模版是用於生成 html 資料的.
我給出一個xls.erb 模版例子:
<?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"><%= t('id', default: 'ID') %></Data></Cell> <Cell><Data ss:Type="String"><%= t('name', default: 'Name') %></Data></Cell> <Cell><Data ss:Type="String"><%= t('date', default: 'Date') %></Data></Cell> <Cell><Data ss:Type="String"><%= t('number', default: 'Number') %></Data></Cell> </Row> <% if @report %> <% @report.items.each do |item| %> <Row> <Cell><Data ss:Type="String"><%= item['id'] %></Data></Cell> <Cell><Data ss:Type="String"><%= item['name'] %></Data></Cell> <Cell><Data ss:Type="String"><%= item['date'].to_s.to_local_date %></Data></Cell> <Cell><Data ss:Type="String"><%= item['number'] %></Data></Cell> </Row> <% end %> <% end %> </Table> </Worksheet> </Workbook>
解析 xls.erb 模版
xls.erb 模版渲染後就是 xml 資料, 我寫了一個 service 類專門用於解析 xls.erb 模版, 然後返回一個 Axlsx::Package 物件
class ParseXmlToXlsxService def initialize(xml) @xml = xml @ap = Axlsx::Package.new end def call h = Hash.from_xml(@xml) ws_list = h['Workbook']['Worksheet'] if ws_list.is_a?(Hash) ws_list = [ws_list] end ws_list.each {|ws| add_sheet(@ap, ws) } @ap.use_shared_strings = true @ap end private def add_sheet(ap, ws) name = ws['ss:Name'] ap.workbook.add_worksheet(:name => name) do |sheet| add_rows(sheet, ws) end end def add_rows(sheet, ws) ws['Table']['Row'].each {|item| row = [] item['Cell'].each {|cell| if cell['Data'].is_a?(Hash) row << '' else row << cell['Data'] end } sheet.add_row(row) } end end
渲染 xlsx
在 ApplicationController 增加一個方法:render_xlsx
,
def render_xlsx(filename, template = nil) template ||= File.join(controller_name, "#{action_name}.xls.erb") xml = view_context.render(template: template) ap = ParseXmlToXlsxService.new(xml).call send_data ap.to_stream.read, :type => :xlsx, :filename => filename end
rendex_xlsx
比較方便的地方是它可以自動找到你需要渲染的 xls.erb 模版
實際使用
首先要在config/initializers/mime_types.rb
這個檔案中,註冊xlsx
型別,
Mime::Type.register "application/xlsx", :xlsx
然後, 假設我們要生成 order report,
class OrdersController < ApplicationController def index @orders = Order.all format.html format.xlsx do render_xlsx('orders_report.xlsx') end end end
在控制器裡對應的 action 中增加一個 format.xlsx 即可, 這樣我們只需作很少的改動,仍然可以利用orders/index.xls.erb
模版來生成一個名為orders_report.xlsx
的 excel 報表.