Rubyで画像ファイルが破損していないかチェックする

Tumblrの画像を取りまくるツールを書いていて、ふと「ちゃんと画像が取れているのか?」と気になったのでチェックするコードを書いてみた。

アルゴリズムというか考え方
  • JPEG については、ファイルが "0xFF 0xD8" で始まり、"0xFF 0xD9" で終わることをチェック。
  • GIF については、ファイルが "GIF" で始まり、Trailer "0x3B" で終わることをチェック。
  • PNG については、ファイルがPNGファイルシグネチャで始まり、IENDで終わることをチェック。
チェックコード
# result : [ filetype, result ]
#   filetype ::= ( :gif, :jpg, :png, :unknown )
#   result   ::= ( :damaged, :clean )
def check_file(filename)
  result = [ :unknown, :clean ]  
  File.open(filename, "rb") do |f|
    begin
      header = f.read(8)
      f.seek(-12, IO::SEEK_END)
      footer = f.read(12)
    rescue
      result[1] = :damaged
      return result
    end

    if header[0,2].unpack("H*") == [ "ffd8" ]
      result[0] = :jpg
      result[1] = :damaged unless footer[-2,2].unpack("H*") == [ "ffd9" ]
    elsif header[0,3].unpack("A*") == [ "GIF" ]
      result[0] = :gif
      result[1] = :damaged unless footer[-1,1].unpack("H*") == [ "3b" ]
    elsif header[0,8].unpack("H*") == [ "89504e470d0a1a0a" ]
      result[0] = :png
      result[1] = :damaged unless footer[-12,12].unpack("H*") == [ "0000000049454e44ae426082" ]
    end
  end
  result
end