You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails项目替换to_xls gem生成xlsx文件的方案咨询

从to_xls切换到xlsx格式生成方案

一、to_xls能否直接生成xlsx?

不行。to_xls依赖的spreadsheet库仅支持旧版的.xls格式(BIFF8标准),完全不支持基于OpenXML的.xlsx格式,必须更换gem实现需求。

二、推荐的xlsx生成gem

  • axlsx:功能完整、文档完善的xlsx生成库,支持样式、多sheet、公式等特性,是Rails项目中生成xlsx的主流选择。
  • roo:主要定位是读取各类电子表格格式,生成xlsx的能力有限,不推荐用于生成场景。
  • 额外可选:axlsx_rails,是axlsx针对Rails的封装,可进一步简化响应处理逻辑。

三、代码修改指导

基于你的现有代码,改用axlsx重写核心逻辑:

首先在Gemfile中添加依赖:

gem 'axlsx'

执行bundle install完成安装。

然后修改响应器代码:

class ToXmlResponder < Dossier::Responder
  def to_xlsx
    set_content_disposition!
    controller.response_body = to_xlsx_raw
  end

  def to_xlsx_raw
    Axlsx::Package.new do |p|
      # 写入内容工作表
      add_content_sheet(p.workbook)
      # 写入元数据工作表
      add_metadata_sheet(p.workbook)

      response = StringIO.new
      p.serialize(response)
      response.string.force_encoding('binary')
    end
  end

  def add_content_sheet(workbook)
    xls_opts = collection_and_headers(resource.raw_results.arrays)
    headers = xls_opts[1]
    items = resource.results.hashes
    columns = resource.raw_results.headers

    workbook.add_worksheet(name: '内容') do |sheet|
      # 写入表头
      sheet.add_row headers
      # 逐行写入数据
      items.each do |item|
        sheet.add_row columns.map { |col| item[col] }
      end
    end
  end

  def add_metadata_sheet(workbook)
    metadata = [
      {
        name: I18n.t('report_info.start_date'),
        value: resource.start_date_in_timezone(resource.user_time_zone).to_s
      },
      {
        name: I18n.t('report_info.end_date'),
        value: resource.end_date_in_timezone(resource.user_time_zone).to_s
      },
      {
        name: I18n.t('report_info.export_by'),
        value: resource.user.username.to_s
      },
      {
        name: I18n.t('report_info.export_date'),
        value: Time.now.in_time_zone(resource.user_time_zone).to_s
      }
    ].reject { |r| r[:value].empty? }

    workbook.add_worksheet(name: I18n.t('report_info.sheet_name')) do |sheet|
      metadata.each do |row|
        sheet.add_row [row[:name], row[:value]]
      end
    end
  end

  private

  def set_content_disposition!
    filename = "report_#{Time.now.strftime('%Y%m%d')}.xlsx"
    controller.response.headers['Content-Disposition'] = "attachment; filename=\"#{filename}\""
    controller.response.headers['Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  end
end

修改说明:

  1. 核心逻辑替换:用Axlsx::Package替代原有的Spreadsheet::Workbook,这是axlsx的核心生成对象。
  2. 拆分工作表创建:将原有的xls_content和xls_metadata拆分为独立的工作表创建方法,逻辑更清晰。
  3. 响应头更新:调整Content-Type为xlsx对应的MIME类型,同时修正文件名后缀。
  4. 简化数据处理:无需再用Hashie::Mash包装数据,直接通过原始哈希取数即可。

四、额外注意事项

  • 如果需要自定义样式(如表头加粗、日期格式设置),axlsx支持丰富的样式配置,可在add_row时传入style参数实现。
  • 处理大文件导出时,axlsx支持流式输出,可避免内存占用过高,具体可参考官方文档实现。

内容的提问来源于stack exchange,提问作者Dev V

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 17:30:25