Find jpeg dimensions fast in pure Ruby, no image library needed

It so happens that in our application we need to find the dimensions of many images that do not reside on our rails application server.  I have previously written about how to use the GD library to find image sizes, but this requires fetching the whole file and having it available in the local file system.Fortunately we know all these files are jpegs, and if you check the jpeg documentation you can see that the dimensions of an image are normally contained near the start, which is ideal.  We will just fetch enough of the image to get the dimensions, and no more.How can we do that?  The get method from Net::HTTP will do the trick – given a block it will yield each packet of data from the remote server as it arrives. And fetching over http makes this method very general – we can size any jpeg from anywhere using this code.Parsing the returned data for the dimensions requires a simple state machine which will break from the http get block once the dimensions are located.So I ended up with a small class to encapsulate the dimension information from the jpeg, JpegDimensions. Here’s how to use it:

jpg_info = JpegDimensions.new("http://somewhere.co.abc/an_image.jpg")jpg_info.height  # is the heightjpg_info.width  # is the width??

And the best thing is that only a small part of the image will be fetched, saving time and bandwith.Here’s the code

require 'net/http'class JpegDimensions  attr_reader :width, :height  def initialize(image_path)    @uri_split = URI.split(image_path)    find_jpeg_size  end  def find_jpeg_size    begin      http = Net::HTTP.new(@uri_split[2], @uri_split[3])      state = 0      http.get(@uri_split[5]) do |str|  # this yields strings as each packet arrives        str.each_byte do |b|          state = case state          when 0            b == 0xFF ? 1 : 0          when 1            b >= 0xC0 && b <= 0xC3 ? 2 : 0          when 2            3          when 3            4          when 4            5          when 5            @height = b * 256            6          when 6            @height += b            7          when 7            @width = b * 256            8          when 8            @width += b            break          end        end        break if state == 8  # don't need to fetch any more of the image      end    rescue Exception=>e      # I do nothing here, but you can do something more useful with the exception if required    end  endend
Advertisements