You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

上传.xlsx到AWS S3 Bucket时出现NilClass未定义bucket方法报错

问题根因与修复方案

根因梳理

  • S3资源未初始化:你在upload_file_to_bucket?方法中直接读取@s3_resource实例变量,但该变量仅在调用s3_resource方法时才会初始化,未调用方法直接取变量导致s3赋值为nil,调用bucket方法时触发报错。
  • 参数传递顺序错位:
    方法定义为def upload_file_to_bucket?(file_name, path, bucket_name),但你在call方法中调用时的参数顺序为upload_file_to_bucket?(bucket_name, file_name, path ),三个参数完全颠倒,方法内拿到的bucket_name实际为未赋值的nil值。
  • 桶名逻辑错误:你定义的BUCKET_NAME方法从未被调用,且方法内错误使用了不存在的s3变量(正确方法名为s3_resource),导致@bucket_name全程为nil。
  • 实例变量未赋值:@file_name、@path两个实例变量从未被赋值,调用时传入的均为nil,因此报错日志中文件名、桶名均显示为空。

修复代码

attr_reader :year,  :month, :data, :bucket_name

def initialize(year:, month:, data:)
  @year = year
  @month = month
  @data = data
  # 初始化时就确定桶名,避免后续传参混乱
  @bucket_name = 'textract-console-eu-central-1-3469d743-084a-020378550'
end

def call
  REPORTS.each do |report|
    data =  generate_report_data(report)
    tmp_path = report_tmp_path(report)
    save_report_to_file(data: data, path: tmp_path)
    # 修正参数顺序,传入正确的文件名、路径、桶名
    upload_file_to_bucket?(report, tmp_path, bucket_name )
  end
end


def generate_report_data(report)
  report_class = "Accounting::Datev::Reports::#{report.to_s.camelize}".constantize
  report_class.new(year: year, month: month).call
end

def save_report_to_file(data:, path:)
  prepare_dir(path)
  Accounting::Datev::Utils::ExcelGenerator.new(data: data, path: path).call
end


def s3_resource
  @s3_resource ||= ::Aws::S3::Resource.new(access_key_id: Settings.aws_pair&.app_key_id,
                                           secret_access_key: Settings.aws_pair&.app_secret_key,
                                           region: Settings.aws_pair&.s3_region)
end

def upload_file_to_bucket?(file_name, path, bucket_name)
  # 直接调用s3_resource方法,确保实例变量已初始化
  s3 = s3_resource
  # 修正object的key,不要用本地路径,改为你想要存在S3上的路径
  obj = s3.bucket(bucket_name).object("reports/#{year}/#{month}/#{file_name}.xls")
  obj.upload_file(path)

  true
rescue StandardError => e
  puts "Error while uploading the file '#{file_name}' to the bucket '#{bucket_name}': #{e.message}"
end


def report_tmp_path(report)
  "/tmp/reports/#{year}/#{month}/#{report}.xls"
end

def prepare_dir(path)
  dir = File.dirname(path)
  FileUtils.mkdir_p(dir)
end

额外注意点

  • 原代码中S3对象的key写死为/tmp/test.xls,会导致所有上传的文件都覆盖同一个文件,已在修复代码中调整为按年月和报表名区分的路径。
  • 不要直接读取带@的实例变量来获取懒加载的资源,一定要调用对应的方法确保资源已初始化。

内容的提问来源于stack exchange,提问作者cbln1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 06:18:02