Photo by Cristian Escobar on Unsplash
Rails 5.2 introduces Active Storage, which can replace external file uploading gems like CarrierWave, PaperClip or Shrine. There are some helpful articles and tutorials about it, e.g. by Evil Martians, GoRails or Drifting Ruby. I want to demonstrate how to add one more feature.
Active Storage in Rails 5.2.0 is just the beginning. Recently, Pull Request #32471 by Janko Marohnić (the author of Shrine) was merged, which allows us to use the ImageProcessing gem instead of mini_magick. This results in faster image processing, automatic orientation, thumbnail sharpening and more.
Active Storage can extract metadata from uploaded images, but currently, this means width
and height
only. One thing I’m missing so far is extracting GPS location (latitude / longitude / altitude) from the Exif part. The following describes how this feature can be added by using the gem exifr.
In Active Storage there are Analyzers to extract metadata – for every file type there is a separate analyzer – currently for images and videos only, but you can add your own. For images, there is the ImageAnalyzer. Because of the internal structure of this class, the only way of enhancement seems to be monkey patching.
First, add this to your Gemfile:
Then, add the file config/initializers/exif.rb
with this content:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
require 'exifr/jpeg'
module ActiveStorage
class Analyzer::ImageAnalyzer < Analyzer
def metadata
read_image do |image|
if rotated_image?(image)
{ width: image.height, height: image.width }
else
{ width: image.width, height: image.height }
end.merge(gps_from_exif(image) || {})
end
rescue LoadError
logger.info "Skipping image analysis because the mini_magick gem isn't installed"
{}
end
private
def gps_from_exif(image)
return unless image.type == 'JPEG'
if exif = EXIFR::JPEG.new(image.path).exif
if gps = exif.fields[:gps]
{
latitude: gps.fields[:gps_latitude].to_f,
longitude: gps.fields[:gps_longitude].to_f,
altitude: gps.fields[:gps_altitude].to_f
}
end
end
rescue EXIFR::MalformedImage, EXIFR::MalformedJPEG
end
end
end
That’s all. Now for every processed image the GPS location data (if there is any) will be extracted as metadata and stored to the database in the table active_storage_blobs
.