iex> defmodule WebDB.Binary do
...> def mime(<<0x89, "PNG", _::bits>>), do: "png"
...> def mime(<<"GIF", 0x38, _::bits>>), do: "gif"
...> def mime(<<0xFF, 0xD8, _::bits>>), do: "jpg"
...> def mime(_), do: "unsupported file"
...> end
{:module, WebDB.Binary, <<(省略)>>, {:mime, 1}}
iex> {:ok, file} = File.read("image.png")
iex> WebDB.Binary.mime(file)
"png"
iex> WebDB.Binary.mime(1)
"unsupported file"
def mime(<<0x89, "PNG", _::bits>>), do: "png"
なぜ0x89?
以下のサイトにPNGファイルシグネチャについて記載がある
PNG バイナリは、先頭に PNG であることを示す8バイトの識別子があり、その後はチャンクと呼ばれるデータのまとまりが最後まで連なる構造になっています
PNGであることを示す。
16進数で常に、89 50 4E 47 0D 0A 1A 0A
この0x89は16進数で89を示している
そのため同じ値を取得できればそのファイルはPNGであることが判明する
<<>>はバイナリーリテラルを示す
_::bitsは残りのビットをすべて無視する内容となるためPNGファイルでさえあればどんな画像であっても一致する
def mime(<<"GIF", 0x38, _::bits>>), do: "gif"
ここでは引数のGIFが先に来ているがその理由は画像ファイルのマジックナンバーが以下のようになっているためである
残りのロジックは先ほどと同様
def mime(<<0xFF, 0xD8, _::bits>>), do: "jpg"
JPGの場合は以下
以下の記事のコードを参考にした
実験
実際に検証してみる
検証のためにubuntu上へそれぞれの拡張子の画像ファイルを作成した
iex経由で実行を行い正しく取得できているかを確認する