【发布时间】:2020-01-21 21:48:26
【问题描述】:
我正在编写一个包含 Creek 和 .xlsx 文件的脚本,并使用它来更新数据库中产品的价格和重量。 .xlsx 文件位于 AWS 服务器上,因此 Creek 将文件复制下来并在使用时将其存储在 Tempfile 中。
问题是,在某些时候 Tempfile 似乎被过早地删除了,并且由于 Creek 会在它遍历工作表时继续调用它,因此脚本会失败。有趣的是,我同事的环境可以很好地运行脚本,尽管我没有发现我们正在运行的环境之间存在差异。
这是我写的脚本:
require 'creek'
class PricingUpdateWorker
include Sidekiq::Worker
def perform(filename)
# This points to the file in the root bucket
file = bucket.files.get(filename)
# Make public temporarily to open in Creek
file.public = true
file.save
creek_sheets = Creek::Book.new(file.public_url, remote: true).sheets
# Close file to public
file.public = false
file.save
creek_sheets.each_with_index do |sheet, sheet_index|
p "---------- #{sheet.name} ----------"
sheet.simple_rows.each_with_index do |row, index|
next if index == 0
product = Product.find_by_id(row['A'].to_i)
if product
if row['D']&.match(/N\/A/) || row['E']&.match(/N\/A/)
product.delete
p '*** deleted ***'
else
product.price = row['D']&.to_f&.round(2)
product.weight = row['E']&.to_f
product.request_for_quote = false
product.save
p 'product updated'
end
else
p "#{row['A']} | product not found ***"
end
end
end
end
private
def connection
@connection ||= Fog::Storage.new(
provider: 'AWS',
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
)
end
def bucket
# Grab the file from the bucket
@bucket ||= connection.directories.get 'my-aws-bucket'
end
end
还有日志:
"---------- Sheet 1 ----------"
"product updated"
"product updated"
... I've cut out a bunch more of these...
"product updated"
"product updated"
"---------- Sheet 2 ----------"
rails aborted!
Errno::ENOENT: No such file or directory @ rb_sysopen - /var/folders/9m/mfcnhxmn1bqbm6h91rx_rd8m0000gn/T/file20190920-19247-c6x4zw
"/var/folders/9m/mfcnhxmn1bqbm6h91rx_rd8m0000gn/T/file20190920-19247-c6x4zw" 是临时文件,如你所见,已经被收集了,虽然我还在用,我也信仍在范围内。有什么想法可能导致这种情况吗?特别奇怪的是我的同事可以很好地运行它。
如果有帮助,这里有一段来自 Creek 的小代码:
def initialize path, options = {}
check_file_extension = options.fetch(:check_file_extension, true)
if check_file_extension
extension = File.extname(options[:original_filename] || path).downcase
raise 'Not a valid file format.' unless (['.xlsx', '.xlsm'].include? extension)
end
if options[:remote]
zipfile = Tempfile.new("file")
zipfile.binmode
zipfile.write(HTTP.get(path).to_s)
# I added the line below this one, and it fixes the problem by preventing the file from being marked for garbage collection, though I shouldn't need to take steps like that.
# ObjectSpace.undefine_finalizer(zipfile)
zipfile.close
path = zipfile.path
end
@files = Zip::File.open(path)
@shared_strings = SharedStrings.new(self)
end
编辑:有人想确切地知道我是如何运行我的代码的,所以就在这里。
我通过在命令行中执行bundle exec rails client:pricing_update[client_updated_prices.xlsx] 来运行以下 rake 任务。
namespace :client do
desc 'Imports the initial database structure & base data from uploaded .xlsx file'
task :pricing_update, [:filename] => :environment do |t, args|
PricingUpdateWorker.new.perform(args[:filename])
end
end
我还应该提到我正在运行 Rails,因此 Gemfile.lock 使我和我的同事之间的 gem 版本保持一致。我的雾版本是 2.0.0,我的 rubyzip 版本是 1.2.2。
【问题讨论】:
-
如果将
# Close file to public之后的两行移动到循环结束后会发生什么? -
lacostenycoder 恐怕没有什么不同。
-
我删除了我的答案,因为在使用公共 xlsx 文件进行了更多测试之后,我能够获得一个更基本的示例来正常工作。我能问一下你是如何运行你的代码的吗?另外,您正在运行什么版本的 Fog 和 rubyzip gem?确保它们与 Heroku 或工作机器上运行的相同。
-
@lacostenycoder 我已经用你的问题的答案更新了我的帖子。
-
如果你是从 rake 运行它而不使用
.perform_async你甚至需要include Sidekiq::Worker吗?但我不认为这是你的问题。