【问题标题】:Rails 5.2 Import XLSX with ActiveStorage and CreekRails 5.2 使用 ActiveStorage 和 Creek 导入 XLSX
【发布时间】:2018-08-13 04:14:11
【问题描述】:

我有一个名为ImportTemp 的模型,用于将导入的 XLSX 文件存储到数据库。我正在使用 ActiveStorage 来存储文件。

这是型号代码:

class ImportTemp < ApplicationRecord
  belongs_to :user
  has_one_attached :file
  has_one_attached :log_result
end

这是我的导入控制器代码:

def import
  # Check filetype
  case File.extname(params[:file].original_filename)
  when ".xlsx"
    # Add File to ImportFile model
    import = ImportTemp.new(import_type: 'UnitsUpload', user: current_user)
    import.file.attach(params[:file])
    import.save

    # Import unit via sidekiq with background jobs
    ImportUnitWorker.perform_async(import.id)

    # Notice
    flash.now[:notice] = "We are processing your xlsx, we will inform you after it's done via notifications."
    # Unit.import_file(xlsx)
  else flash.now[:error] = t('shared.info.unknown')+": #{params[:file].original_filename}"
  end
end

上传xlsx文件后,导入将在sidekiq中处理。这是工作代码(实际上仍然不执行导入):

class ImportUnitWorker
  include Sidekiq::Worker
  sidekiq_options retry: false

def perform(file_id)
  import_unit = ImportTemp.find(file_id)

  # Open the uploaded xlsx to Creek
  creek = Creek::Book.new(Rails.application.routes.url_helpers.rails_blob_path(import_unit.file, only_path: true))
  sheet = creek.sheets[0]

  puts "Opening Sheet #{sheet.name}"
  sheet.rows.each do |row|
    puts row
  end

  units = []

  # Unit.import(units)
end

但是在我尝试之后,它给了我错误:

Zip::Error (File /rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--3960b6ba5b55f7004e09967d16dfabe63f09f0a9/2018-08-10_10_39_audit_gt.xlsx not found)

但如果我尝试用我的浏览器打开它,链接如下所示:

http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--3960b6ba5b55f7004e09967d16dfabe63f09f0a9/2018-08-10_10_39_audit_gt.xlsx

它正在工作并且 xlsx 已下载。我的问题是它有什么问题?为什么在sidekiq中找不到文件?

【问题讨论】:

    标签: ruby-on-rails ruby import sidekiq rails-activestorage


    【解决方案1】:

    按照 George Claghorn 的建议,我最终使用了 Tempfile。我不知道这是否是最佳解决方案或最佳实践,但它现在对我有用。我将在等待 Rails 6 stable 推出 ActiveStorage::Blob#open 功能时使用此解决方案。

    def perform(file_id)
      import    = ImportTemp.find(file_id)
      temp_unit = Tempfile.new([ 'unit_import_temp', '.xlsx' ], :encoding => 'ascii-8bit')
      units     = []
    
      begin
        # Write xlsx from ImportTemp to Tempfile
        temp_unit.write(import.file.download)
    
        # Open the temp xlsx to Creek
        book   = Creek::Book.new(temp_unit.path)
        sheet  = book.sheets[0]
    
        sheet.rows.each do |row|
          # Skip the header
          next if row.values[0] == 'Name' || row.values[1] == 'Abbreviation'
          cells = row.values
    
          # Add cells to new Unit
          unit = Unit.new(name: cells[0], abbrev: cells[1], desc: cells[2])
          units << unit
        end
    
        # Import the unit
        Unit.import(units)
      ensure
        temp_unit.close
        temp_unit.unlink   # deletes the temp file
      end
    end
    

    【讨论】:

    • 谢谢,我找了这么久,我也在使用 rails 5。所以这个旁路非常好用!
    • 不客气,我很高兴它可以帮助某人/任何人
    【解决方案2】:

    Rails.application.routes.url_helpers.rails_blob_path 不返回磁盘上文件的路径。相反,它返回一个路径,该路径可以与主机名组合以生成用于下载文件的 URL,以用于链接。

    你有两个选择:

    • 如果您希望 ImportUnitWorker 不关心正在使用的存储服务,请将文件“下载”到磁盘上的临时文件中。切换到 Rails master 并使用 ActiveStorage::Blob#open:

      def perform(import_id)
        import = ImportTemp.find(import_id)
        units  = []
      
        import.file.open do |file|
          book  = Creek::Book.new(file.path)
          sheet = creek.sheets[0]
      
          # ...
        end
      
        Unit.import(units)
      end
      
    • 如果您不介意ImportWorker 知道您使用磁盘服务,请向该服务询问磁盘上文件的路径。 ActiveStorage::Service::DiskService#path_for(key) 在 Rails 5.2 中是私有的,所以要么用 send 强行调用它,要么升级到公开的 Rails master:

      def perform(import_id)
        import = ImportTemp.find(import_id)
        units  = []
      
        path   = ActiveStorage::Blob.service.send(:path_for, import.file.key)
        book   = Creek::Book.new(path)
        sheet  = creek.sheets[0]
      
        # ...
      
        Unit.import(units)
      end
      

    【讨论】:

      【解决方案3】:

      现在的答案似乎是(除非我遗漏了什么):

      Creek::Book.new file.service_url, check_file_extension: false, remote: true
      

      【讨论】:

        猜你喜欢
        • 2019-05-19
        • 2020-05-30
        • 2019-10-23
        • 1970-01-01
        • 2019-06-12
        • 2018-02-02
        • 2019-05-19
        • 1970-01-01
        相关资源
        最近更新 更多