我有一个 Ruby on Rails 应用程序,希望使用它导入用户定义的 CSV 文件,操作该文件的某些方面,然后使用它在我的数据库中创建对象。我的控制器操作看起来像这样:
def validate_annual_sales
uploaded_file = params[:client][:annual_sales_file]
filename = filename_with_timestamp(uploaded_file.original_filename)
file_path = Rails.root.join('files', 'uploads', filename)
File.open(file_path, 'wb') { |file| file.write(uploaded_file.read) }
@fyear = 2024
@season_desc = "Season 2024"
handler = SpektrixReportHandler.new(@client.id, file_path.to_s, @fyear, @season_desc)
@validation_errors = handler.validate_csv
unless @validation_errors.any?
# perform import
handler.translate_csv_to_data
File.delete(file_path) if File.exist?(file_path)
redirect_to historicals_client_url(@client), notice: 'Annual Sales Report information successfully imported.'
else
end
rescue => ex
AppSignaler.set_client_error(ex, @client.try(:id))
File.delete(file_path) if File.exist?(file_path)
redirect_to upload_annual_sales_client_url(@client), alert: ex.message
end
annual_sales_file
参数是视图中的file_field
,我们在应用程序的其他部分中出于相同目的使用它,效果很好。与打开和读取文件类似。
我处理这种情况的服务对象的相关部分是:
def validate_csv
json_schema_validator = mapping.dig(:schema_validator)
errors = []
get_parsed_csv.each.with_index(1) do |row, index|
data = row.to_hash
json_errors = JSON::Validator.fully_validate(json_schema_validator, data.to_json, errors_as_objects: true)
if json_errors.empty?
# don't try to validate the values if the basic json validations failed
csv_validator = CsvValidator.validate(data, mapping)
unless csv_validator[:errors].try(:empty?)
errors << ["Error processing row #{index}:"] + csv_validator[:errors]
end
else
cleaned_errors = clean_json_errors(json_errors)
errors << ["Error processing row #{index}:"] + cleaned_errors
end
end
errors
end
def translate_csv_to_data
blank_values = [:fiscal_year, :season_description].select{ |attr_name| self.send(attr_name).blank? }.map{|value| value.to_s.titleize}
if blank_values.any?
raise "Missing #{blank_values.to_sentence}"
end
get_parsed_csv.each.with_index(1) do |row, index|
ap "row #{index}"
hashed_row = row.to_hash
row = parser_factory.parse(hashed_row, mapping)
row[:client_id] = client.id
row[:fyear] = fiscal_year
row[:season_desc] = season_description
row[:sin_amount] = row.delete(:sin_amt)
row[:pkg_amount] = row.delete(:pkg_amt)
HistoricalDaily.create!(row)
end
ensure
File.delete(file_path) if File.exist?(file_path)
end
private
# from EmailScrapperLogger#get_parsed_csv
def get_parsed_csv
CSV.parse(File.read(csv, encoding: 'bom|utf-8'), headers: true, force_quotes: true, header_converters: :symbol)
end
但是,当我实际选择一个文件并运行它时,我收到一个
ActionDispatch::Cookies::CookieOverflow
异常,导致页面崩溃。
据我所知,我根本没有操作给定的文件,所以我对 cookie 的更改位置感到非常困惑。我尝试将所有内容作为哈希加载到内存中,并从该数据手动创建对象。我尝试将其作为 zip 文件加载,将 csv 文件完全提取到另一个目录中,然后从那里开始。我们在其他地方就是这样做的,而且看起来效果很好。不确定我在这里做了什么不同的事情。
请帮忙!
我终于明白了这一点。正如评论中提到的,这确实是完全不同的东西,与直接操作 cookie 甚至会话无关。
发生的事情是其他地方发生了错误。一旦您看到消息和回溯,这些愚蠢的 NoMethodErrors 就会非常明显。本例的困难在于控制器中有一行代码向浏览器打印错误消息。它将该消息放入 flash 对象中,该对象是会话对象的一部分,也算作 cookie。因此,当抛出该错误并将天真的
error.message
放入 flash 对象时,模型对象的序列化版本也放入其中,很快就溢出了 cookie 限制。非常间接且非常微妙。
回想起来,我用这行控制器代码在我的问题中包含了答案的提示:
redirect_to upload_annual_sales_client_url(@client), alert: ex.message
乍一看很无辜,我或好心花时间在这里发表评论的人没有注意到危险和影响(我感谢你!)。希望其他人也会遇到这个问题并发现这有帮助!