Rails 7 ActiveStorage从URL传图遇FileNotFoundError求助
问题分析
你遇到的ActiveStorage::FileNotFoundError,本质是文件上传过程中流处理或类型推断异常,导致实际存储的文件无法被Representation(缩略图生成)功能正确识别。虽然数据库里Blob和Attachment条目存在,但存储服务里的文件可能未正确写入,或者元数据(如content_type)缺失/错误。
第一种方法失败的原因
open方法的潜在问题
你用open()读取本地文件,若环境中引入了URI模块,open会指向URI.open,当传入本地路径时可能触发错误的解析逻辑;即使是Kernel#open,未指定二进制模式打开文件,也可能导致文件内容被转义,ActiveStorage无法正确写入存储。content_type自动推断失败
attach方法依赖从io对象推断文件的content_type,如果源文件的格式无法被自动识别,Blob的content_type字段会是nil或错误值,后续生成Representation时(比如访问redirect URL),ActiveStorage无法找到对应的处理器,最终抛出文件找不到的错误。事务回调的延迟问题
在User的after_create回调中执行创建Mainphoto和attach操作,若整个回调处于User创建的事务中,attach完成后事务未提交,可能导致Blob的元数据未完全同步到存储服务。
第二种方法有效的原因
你手动调用ActiveStorage::Blob.create_and_upload!和ActiveStorage::Attachment.create,相当于绕开了has_one_attached的自动封装:
create_and_upload!直接同步完成Blob创建和文件上传,避免了事务延迟问题;- 手动指定了
content_type: 'image',强制设置了文件元数据,确保Representation功能能正确识别文件类型; - 用
File.open直接读取本地文件,避免了open方法的不确定性。
解决方案
修改你的代码,解决文件读取和类型推断问题:
Mainphoto.create!(profile_id: self.profile.id) # 用File.open以二进制模式读取本地文件 file = File.open("#{Rails.root}/public" + self.profile.avatar_photo_url, 'rb') photo_url = self.profile.avatar_photo_url self.profile.mainphoto.main_photo.attach( io: file, filename: photo_url.slice(8..), content_type: 'image/jpeg' # 根据实际图片类型调整,比如image/png ) # 手动关闭文件流 file.close
额外检查点
- 确认
config/storage.yml的本地存储配置正确:
local: service: Disk root: <%= Rails.root.join("storage") %>
- 检查public目录下的源文件是否存在,且Rails进程有读取权限;
- 查看数据库中Blob表的
content_type字段,确保第一种方法生成的记录有正确的类型值。
内容的提问来源于stack exchange,提问作者Wordica

