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
相关产品推荐
相关产品推荐

