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 height
jpg_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
  end
end

Blog Archives

Navigation


About this blog

A blog about Ruby, Rails and other tech. Mostly.


Find Something?