我正在使用后台作业,以便将用户数据从csv文件导入到我的数据中。首先我做了这个"很难"在我的用户模型中,只需调用我的用户模型中的方法,并传递通过表单file_field
传输的文件路径:
User.import_csv(params[:file].path)
在本地和生产(heroku)工作得很好。
现在谈到巨大的CSV文件,我明白我需要一份工作才能在后台执行此导入。我熟悉redis和sidekiq,所以工作很快就建好了。
CsvImportJob.perform_async(URI.parse(params[:file].path))
在我的工作人员中:
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这样做时,它可以工作。
我真的不知道如何解决这个问题,所以感谢任何帮助。
答案 0 :(得分:1)
我有同样的经历,您可以在https://github.com/coderaven/datatable-exercise/tree/parallel_processing
查看我的类似项目(基本上只关注object_record.rb模型和作业:import_csv_job.rb和process_csv_job.rb)
错误: Errno :: ENOENT:没有这样的文件或目录@ rb_sysopen 如果你说这可以在heroku上运行那么可能这意味着你获得它的路径是有效的(在你的例子中你使用的是/ tmp / path)
所以这里有两个可能的问题及其解决方案:
1。)您已将未知的Heroku路径(或不可访问的路径)保存到应用程序运行时无法访问或打开。因为,当处理导入csv时没有sidekiq - 您上传的文件暂时保存在内存中,直到您完成处理csv为止 - 但是,在作业调度程序(或sidekiq)中,路径不应该在内存中,并且应该是应用程序可访问的现有路径。
解决方案:将文件保存到某个存储位置(heroku有一个短暂的文件系统,因此您无法通过正在运行的Web应用程序保存文件)来解决这个问题,您必须使用类似Amazon S3的服务(您也可以使用Google像我那样开车)将文件保存在那里,然后给你的sidekiq工作者提供路径 - 这样它就可以在以后访问和处理它。
2。)如果路径正确并且文件保存或处理正确,那么从我的经验可能是您使用File.open而不是open-uri的open方法。 File.open不接受远程文件,你需要在你的worker上使用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
我完全清楚这个问题已经过去了将近一年,所以如果你已经解决了这个问题,或者这个答案有效,那么它也可以作为文档存档帮助那些可能会遇到同样问题的人。
答案 1 :(得分:0)
您无法将文件对象传递给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为Sidekiq调用CsvImportJob.perform_later
。
答案 2 :(得分:0)
您收到此错误,是因为在生产/登台和sidekiq上运行在不同的服务器上。 使用我的解决方案:将csv上传到Google云存储
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)