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

Rails ActiveStorage与Sidekiq:异步任务中.attach方法失效原因

问题原因及解决方案

核心原因

你遇到的FileNotFoundError本质是异步任务中临时文件被过早删除:

  • 当用perform_inline时,任务在主进程同步执行,obj.poster.attach完成文件读取/上传后,才会执行ensure块里的file.unlink,不会有问题。
  • 当用perform_async时,Sidekiq worker进程独立运行,虽然代码里attach调用在前、unlink在后,但Active Storage的attach操作可能存在底层异步逻辑(比如云存储的后台上传),或者文件读取存在微小延迟,导致unlink先于文件读取完成执行,后续Active Storage尝试访问文件时就会触发找不到文件的错误。

另外,还有一个潜在风险:你直接将ActiveRecord对象obj作为perform_async的参数,虽然Rails的GlobalID支持对象序列化,但这种方式可能导致实例状态不一致,建议改为传递对象ID,在任务内部重新查询。

修复方案

1. 使用Tempfile的块式语法(推荐)

Tempfile的块式写法会在代码块执行完毕后自动关闭并删除文件,确保attach操作完全完成后才清理临时文件:

def attach_poster(poster_url, obj)
  resp = HTTParty.get(poster_url)
  Tempfile.open do |file|
    file.binmode
    file.write(resp.body)
    file.rewind
    obj.poster.attach(io: file, filename: "poster.png")
  end
end

2. 规范Sidekiq任务参数传递

避免直接传ActiveRecord对象,改用对象ID,在任务内部查询实例:

# 调用任务时
ImportJob.perform_async(poster_url, obj.id)

# 任务类的perform方法
def perform(poster_url, obj_id)
  obj = YourModel.find(obj_id) # 替换为你的实际模型类
  attach_poster(poster_url, obj)
end

3. 额外排查方向(如果上述方案无效)

  • 检查Sidekiq进程的运行用户与主进程是否一致,确保/tmp目录(Tempfile默认存储路径)的读写权限无问题。
  • 确认HTTParty获取的响应体完整,可通过resp.success?判断请求是否成功,避免写入空文件导致后续异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 16:56:04