SeedファイルでShrineのuploadを使うと、IOError closed streamとなる場合の対処
Seed ファイルで Shrine の upload
メソッドを使うと、動画や画像を紐付けたサンプルデータを生成できます。以下は、seed_fu
を使ってデータを生成しているコードです。
# 全てのレコードで同じ動画を使用
video_file = File.new(Rails.root.join('spec/fixtures/files/sample.mp4'))
sample_video = VideoUploader.new(:store).upload(video_file)
Hoge.seed do |s|
s.id = 1
s.video_data = sample_video.to_json
end
Hoge.seed do |s|
s.id = 2
s.video_data = sample_video.to_json
end
上記のコードだと全てのレコードで同じ動画を使用しているので、1 つのレコードを削除し紐付いている動画も削除されると、他のレコードも参照先がなくなってしまいます。
なので、下記のように Seed を書き換えてみます。
video_file = File.new(Rails.root.join('spec/fixtures/files/sample.mp4'))
# データ生成のたびに動画をアップロードして使用する
Hoge.seed do |s|
sample_video = VideoUploader.new(:store).upload(video_file)
s.id = 1
s.video_data = sample_video.to_json
end
Hoge.seed do |s|
sample_video = VideoUploader.new(:store).upload(video_file)
s.id = 2
s.video_data = sample_video.to_json
end
しかし、上記のコードだと IOError: closed stream
というエラーによってデータの生成に失敗します。エラーになるタイミングは、2 つ目のレコードを生成するところです。これがなぜなのかを追ってみます。
原因
IOError: closed stream
というエラーの内容から 2 回目に video_file
を参照しようとした際に、close
されていてアップロードができなかったのかなと思いました。
rails c
で確認してみます。
[16] (main)> video_file = File.new(Rails.root.join('spec/fixtures/files/sample.mp4'))
#<File:/xxx/files/sample.mp4>
-rw-r--r-- 1 tanakakenzou staff 3651 Nov 25 11:04 /xxx/files/sample.mp4
[17] (main)> video_file # この時点では参照できることを確認
#<File:/xxx/files/sample.mp4>
-rw-r--r-- 1 tanakakenzou staff 3651 Nov 25 11:04 /xxx/files/sample.mp4
[18] (main)> sample_video = VideoUploader.new(:store).upload(video_file) # upload実行
#<VideoUploader::UploadedFile storage=:store id="01f320594bafdb12f8506477378bee48.mp4" metadata={"filename"=>"sample.mp4", "size"=>3651, "mime_type"=>nil}>
[19] (main)> video_file # ここで参照できなくなっている
(pry) output error: #<IOError: closed stream>
上記の通り、video_file
を参照できなくなっているのは、upload
を実行した後です。なので、upload
の挙動を具体的に追ってみます。
定義されているのはこのあたりです。
shrine/shrine.rb at master · shrinerb/shrine
def upload(io, **options)
_enforce_io(io)
metadata = get_metadata(io, **options)
location = get_location(io, **options, metadata: metadata)
_upload(io, **options, location: location, metadata: metadata)
self.class::UploadedFile.new(
id: location,
storage: storage_key,
metadata: metadata,
)
end
shrine/shrine.rb at master · shrinerb/shrine
def _upload(io, location:, metadata:, upload_options: {}, close: true, delete: false, **)
storage.upload(io, location, shrine_metadata: metadata, **upload_options)
ensure
io.close if close
File.unlink(io.path) if delete && io.respond_to?(:path) && File.exist?(io.path)
end
上記のように _upload
メソッドで io.close
されているため、2 回目以降はファイルを参照できなくなっている、ということが分かりました。
対応
各ブロックの中で、video_file
も定義する形にすると IOError: closed stream
は発生しなくなります。必要であればメソッドに切り出すなどが良いかと思います。
Hoge.seed do |s|
video_file = File.new(Rails.root.join('spec/fixtures/files/sample.mp4'))
sample_video = VideoUploader.new(:store).upload(video_file)
s.id = 1
s.video_data = sample_video.to_json
end
Hoge.seed do |s|
video_file = File.new(Rails.root.join('spec/fixtures/files/sample.mp4'))
sample_video = VideoUploader.new(:store).upload(video_file)
s.id = 2
s.video_data = sample_video.to_json
end