課題
(Rails 6.0.0)下記のようなUserモデルを考える. has_one_attachedでimageを持たせておく.
class User < ApplicationRecord
has_one_attached :image
end
このモデルにコールバックを設定する。
class User < ApplicationRecord
has_one_attached :image
before_save do
filename = ActiveStorage::Blob.service.path_for(image.key)
puts "#{filename} "+(File.exist?(filename) ? "exists": "doesn't exist")
f = File.open(ActiveStorage::Blob.service.path_for(image.key))
end
after_save do
filename = ActiveStorage::Blob.service.path_for(image.key)
puts "#{filename} "+(File.exist?(filename) ? "exists": "doesn't exist")
f = File.open(ActiveStorage::Blob.service.path_for(image.key))
end
after_commit do
filename = ActiveStorage::Blob.service.path_for(image.key)
puts "#{filename} "+(File.exist?(filename) ? "exists": "doesn't exist")
f = File.open(ActiveStorage::Blob.service.path_for(image.key))
end
end
それぞれbefore_save, after_save, after_commitのタイミングでFile.exist?を行い、ファイルの存在確認をしています.
結果
// before_saveのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 doesn't exist"
// after_saveのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 doesn't exist"
// after_commitのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 doesn't exist"
説明
has_one_attached内で下記のコールバックが設定されている.
after_commit(on: %i[ create update ]) { attachment_changes.delete(name.to_s).try(:upload) }
after_commit系のコールバックは逆順(reverse order)で呼び出されるよう.
上記の場合 after_commitを呼び出す -> uploadの順となるためファイルが見つからないらしい.
解決策
①コールバックとhas_one_attachedを逆にする
class User < ApplicationRecord
before_save do
filename = ActiveStorage::Blob.service.path_for(image.key)
puts "#{filename} "+(File.exist?(filename) ? "exists": "doesn't exist")
f = File.open(ActiveStorage::Blob.service.path_for(image.key))
end
after_save do
filename = ActiveStorage::Blob.service.path_for(image.key)
puts "#{filename} "+(File.exist?(filename) ? "exists": "doesn't exist")
f = File.open(ActiveStorage::Blob.service.path_for(image.key))
end
after_commit do
filename = ActiveStorage::Blob.service.path_for(image.key)
puts "#{filename} "+(File.exist?(filename) ? "exists": "doesn't exist")
f = File.open(ActiveStorage::Blob.service.path_for(image.key))
end
has_one_attached :image
end
// before_saveのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 doesn't exist"
// after_saveのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 doesn't exist"
// after_commitのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 exist"
※commitされるまで実ファイルが存在しないのは正しい挙動(Rails 6.0.0~)
参考: https://github.com/rails/rails/pull/33303
②prepend: trueをつける
※prepend_trueについて: https://railsguides.jp/active_record_callbacks.html
class User < ApplicationRecord
has_one_attached :image
after_commit :file_exist_check, prepend: true
private
def file_exist_check
filename = ActiveStorage::Blob.service.path_for(image.key)
puts "#{filename}"+(File.exist?(filename) ? "exists": "doesn't exist")
end
end
// before_saveのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 doesn't exist"
// after_saveのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 doesn't exist"
// after_commitのタイミング
> "/test/storage/ay/9y/ay9yt1l70jmqw1mw20yykkcndb01 exist"
参考
ActiveStorage::FileNotFoundError in before_save · Issue #36994 · rails/rails
Steps to reproduce Example Repo Background I'm using Active Storage with Disk storage. I want users to upload a json file attached to a model called Test_Model....
コメント