Add Metadata
The add_metadata
plugin allows adding custom metadata to
uploaded files.
Shrine.plugin :add_metadata
Metadata block
The Shrine.add_metadata
method allows you to register a block that will get
executed on upload, where you can return custom metadata:
require "pdf-reader" # https://github.com/yob/pdf-reader
class PdfUploader < Shrine
add_metadata :page_count do |io|
reader = PDF::Reader.new(io)
reader.page_count
end
end
The above will add page_count
key to the metadata hash, and also create the
#page_count
reader method on the Shrine::UploadedFile
.
uploaded_file.metadata["page_count"] #=> 30
# or
uploaded_file.page_count #=> 30
Skipping nil values
By default, if your block returns nil
then the nil
value will be stored into
metadata. If you do not want to store anything when your block returns nil, you
can use the skip_nil: true
option:
class PdfUploader < Shrine
add_metadata :pages, skip_nil: true do |io|
if is_pdf?(io)
reader = PDF::Reader.new(io)
reader.page_count
else
# If this is not a PDF, then the pages metadata will not be stored
nil
end
end
end
Multiple values
You can also extract multiple metadata values at once, by using add_metadata
without an argument and returning a hash of metadata.
require "exif" # https://github.com/tonytonyjan/exif
class ImageUploader < Shrine
add_metadata do |io|
begin
data = Exif::Data.new(io)
rescue Exif::NotReadable # not a valid image
next {}
end
{ "date_time" => data.date_time,
"flash" => data.flash,
"focal_length" => data.focal_length,
"exposure_time" => data.exposure_time }
end
end
uploaded_file.metadata #=>
# {
# ...
# "date_time" => "2019:07:20 16:16:08",
# "flash" => 16,
# "focal_length" => 26/1,
# "exposure_time" => 1/500,
# }
In this case Shrine won't automatically create reader methods for the extracted
metadata, but you can create them via Shrine.metadata_method
:
class ImageUploader < Shrine
# ...
metadata_method :date_time, :flash
end
uploaded_file.date_time #=> "2019:07:20 16:16:08"
uploaded_file.flash #=> 16
Ensuring file
The io
might not always be a file object, so if you're using an analyzer
which requires the source file to be on disk, you can use Shrine.with_file
to
ensure you have a file object.
require "streamio-ffmpeg" # https://github.com/streamio/streamio-ffmpeg
class VideoUploader < Shrine
add_metadata do |io|
movie = Shrine.with_file(io) do |file|
FFMPEG::Movie.new(file.path)
end
{ "duration" => movie.duration,
"bitrate" => movie.bitrate,
"resolution" => movie.resolution,
"frame_rate" => movie.frame_rate }
end
end
Uploader options
Uploader options are also yielded to the block, you can access them for more context:
add_metadata do |io, **options|
options #=>
# {
# record: #<Photo>,
# name: :image,
# action: :store,
# metadata: { ... },
# ...
# }
end
Metadata
The :metadata
option holds metadata that was extracted so far:
add_metadata :foo do |io, metadata:, **|
metadata #=>
# {
# "size" => 239823,
# "filename" => "nature.jpg",
# "mime_type" => "image/jpeg"
# }
"foo"
end
add_metadata :bar do |io, metadata:, **|
metadata #=>
# {
# "size" => 239823,
# "filename" => "nature.jpg",
# "mime_type" => "image/jpeg",
# "foo" => "foo"
# }
"bar"
end
Updating metadata
If you just wish to add some custom metadata to existing uploads, you can do it
with UploadedFile#add_metadata
(and write the changes back to the model):
attacher.file.add_metadata("foo" => "bar")
attacher.write # write changes to the model attribute
attacher.file.metadata #=> { ..., "foo" => "bar" }
You can also use the Attacher#add_metadata
shorthand, which also takes care
of syncing the model:
attacher.add_metadata("foo" => "bar")
attacher.file.metadata #=> { ..., "foo" => "bar" }