在Heroku上通过Sidekiq导入CSV时,file.path存在问题

6

我正在使用后台作业来从csv文件中导入用户数据到我的数据库。首先,我在我的User模型中“硬编码”了这个过程,只是通过调用User模型中的一个方法,并传递一个通过表单file_field传输的文件路径:

User.import_csv(params[:file].path)

在本地和生产环境(heroku)中都能很好地工作。

现在,当处理大型CSV文件时,我意识到需要一个后台任务来执行导入。我熟悉redis和sidekiq,因此这个任务很快就被建立了。

CsvImportJob.perform_async(URI.parse(params[:file].path))

在我的 worker 中:
def perform(file_path)

  User.import_csv(file_path)

end

这在本地也可以完美运行,但是一旦在生产环境中使用,就会在我的日志中看到以下错误:

» 10 Aug 2015 13:56:26.596 2015-08-10 11:56:25.987726+00:00 app worker.1 - - 3 TID-oqvt6v1d4 ERROR: Actor crashed!
» 10 Aug 2015 13:56:26.596 2015-08-10 11:56:25.987728+00:00 app worker.1 - - Errno::ENOENT: No such file or directory @ rb_sysopen - /tmp/RackMultipart20150810-6-14u804c.csv
» 10 Aug 2015 13:56:26.596 2015-08-10 11:56:25.987730+00:00 app worker.1 - - /app/vendor/ruby-2.2.2/lib/ruby/2.2.0/csv.rb:1256:in `initialize'

这是变量file_path。当我将其传递给Sidekiq作业时,Heroku无法找到该文件。如果没有使用Sidekiq,则可以正常工作。
我不知道如何解决这个问题,因此需要帮助。
3个回答

1

您不能将文件对象传递给perform方法。

解决方法是预处理数据并直接传递所需的参数。

类似于...

def import_csv(file)
  CSV.foreach(file.path, headers: true) do |row|
    new_user = { email: row[0], password: row[1] }
    CsvImportJob.perform_async(new_user)
  end
end

注意:在使用ActiveJob和Rails 5中,您需要调用CsvImportJob.perform_later来使用Sidekiq。

1
我有同样的经历,你可以看看我的类似项目https://github.com/coderaven/datatable-exercise/tree/parallel_processing
(基本上只关注object_record.rb模型和jobs:import_csv_job.rb和process_csv_job.rb) 错误信息:Errno::ENOENT: No such file or directory @ rb_sysopen。如果你说这在Heroku上是有效的,那么很可能意味着你得到的路径是有效的(在你的例子中,你使用了/tmp/路径)。
因此,这里有两个可能的问题及其解决方案: 1.) 你保存了一个未知的Heroku路径(或不可访问的路径),当应用程序运行时无法访问或打开。 因为,在没有使用sidekiq处理导入csv时,上传的文件会被暂时保存在内存中,直到你完成csv的处理。但是,在作业调度程序(或sidekiq)中,路径不应该在内存中,并且应该是应用程序可以访问的现有路径。
解决方案:将文件保存到某个存储位置(Heroku 有一个临时文件系统,因此您无法通过运行的 Web 应用程序保存文件)为了解决这个问题,您必须使用像 Amazon S3 这样的服务(您也可以像我一样使用 Google Drive)将您的文件保存在那里,然后将路径提供给您的 Sidekiq 工作者 - 这样它就可以稍后访问和处理它。
2.) 如果路径正确并且文件已正确保存或处理,则根据我的经验,可能是因为您正在使用 File.open 而不是 open-uri 的 open 方法。 File.open 不接受远程文件,您需要在工作者中要求 open-uri 然后使用 open 方法来解决远程文件。
例如:
require 'open-uri'

class ProcessCsvJob < ActiveJob::Base
  queue_as :default

  def perform(csv_path)
    csv_file = open(csv_path,'rb:UTF-8')

    SmarterCSV.process(csv_file) do |array|
        .... code here for processing ...
    end
  end

end

我完全意识到这个问题已经过去了将近一年,所以如果您已经解决了这个问题或者这个答案有效,那么它也可以作为文档存档,帮助那些可能遇到同样问题的人。


0
你遇到这个错误是因为生产/暂存和Sidekiq运行在不同的服务器上。 使用我的解决方案:将CSV上传到Google Cloud Storage。
class Services::Downloader
  require 'fog'
  StorageCredentials = YAML.load_file("#{::Rails.root}/config/g.yml")[Rails.env]


  def self.download(file_name, local_path)
    storage = Fog::Storage.new(
        provider: "Google",
        google_storage_access_key_id: StorageCredentials['key_id'],
        google_storage_secret_access_key: StorageCredentials['access_key'])
    storage.get_bucket(StorageCredentials['bucket'])
    f = File.open(local_path)
    storage.put_object(StorageCredentials['bucket'], file_name, f)
    storage.get_object_https_url(StorageCredentials['bucket'], file_name, Time.now.to_f + 24.hours)
  end
end

用户类

class User < ApplicationRecord
  require 'csv'
  require 'open-uri'


  def self.import_data(file)
    load_file =  open(file)
    data = CSV.read(load_file, { encoding: "UTF-8", headers: true, header_converters: :symbol, converters: :all})
...

工作者

class ImportWorker
  include Sidekiq::Worker
  sidekiq_options queue: 'workers', retry: 0

  def perform(filename)

    User.import_data(filename)
  end
end

并且开始工作的代码

--
path = Services::Downloader.download(zip.name, zip.path)
      ImportWorker.perform_async(path)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接