Search code examples
ruby-on-railsrubyimportsidekiqrails-activestorage

Rails 5.2 Import XLSX with ActiveStorage and Creek


I have a model called ImportTemp which is used for store imported XLSX file to database. I'm using ActiveStorage for storing the files.

this is the model code:

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

this is my import controller code:

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

after upload the xlsx file, then the import will be processed in sidekiq. This is the worker code (still doesn't do the import actually) :

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

but after i tried it, it gives me error:

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

but if i tried to open it with my browser, which is the link looks like this:

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

it's working and the xlsx is downloaded. My question is what's wrong with it? why the file is not found in the sidekiq?


Solution

  • I ended up using Tempfile as suggested by George Claghorn. I don't know if this is the best solution or best practices, but it works for me now. I'm going to use this solution while waiting Rails 6 stable to come out with ActiveStorage::Blob#open feature.

    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