MagicBytes (magic_bytes v0.2.0)

Copy Markdown View Source

Detect MIME types from binary content using magic byte signatures.

Only the leading bytes needed to identify the format are examined — 16 bytes satisfies all built-in signatures.

iex> MagicBytes.from_binary(<<0xFF, 0xD8, 0xFF, 0xE0>>)
{:ok, "image/jpeg"}

iex> MagicBytes.from_binary(<<0x00, 0x00, 0x00, 0x00>>)
{:error, :unknown}

Guards

For each signature a corresponding guard macro is generated, named is_<mime_with_slashes_and_hyphens_as_underscores>. These expand to pure boolean expressions and can be used in when clauses or regular code after require MagicBytes:

iex> require MagicBytes
iex> MagicBytes.is_image_jpeg(<<0xFF, 0xD8, 0xFF, 0xE0>>)
true

iex> require MagicBytes
iex> MagicBytes.is_application_pdf(<<?%, ?P, ?D, ?F, ?-, ?1, ?., ?7>>)
true

Guards are not generated for container-format signatures that require inspecting bytes beyond a fixed prefix (WebP, WAV, AVI, AIFF, MP4, HEIC, AVIF, QuickTime). Use from_binary/1 for those.

Custom signatures

Define a module with use MagicBytes.DefineSignatures, configure it once, and all from_* functions will check your signatures first, falling back to the built-ins automatically.

defmodule MyApp.Signatures do
  use MagicBytes.DefineSignatures, guards: true
  defsignature("application/x-cld", <<0xCA, 0xFE, 0xD0, 0x0D>>)
end

# config/config.exs
config :magic_bytes, extra_signatures: MyApp.Signatures

Passing guards: true generates guard macros on your module (e.g. MyApp.Signatures.is_application_x_cld/1) that can be used in when clauses after require MyApp.Signatures.

Configuration

Set in config/config.exs (values are resolved at compile time):

KeyTypeDefaultDescription
:extra_signaturesmodulenilModule with additional signatures
:read_bytespos_integer16Bytes read from input. Only needed if you define custom signatures whose prefix exceeds 16 bytes.
:onlylist(String)nilWhen set, only these MIME types are returned; others become {:error, :unknown}
:excludelist(String)[]MIME types to suppress; ignored when :only is set

Supported formats

CategoryMIME types

| Images | image/jpeg, image/png, image/gif, image/webp, image/bmp, | image/tiff, image/x-icon, image/vnd.adobe.photoshop, | image/heic, image/avif, image/jp2, image/jxl, image/flif | | Audio | audio/mpeg, audio/flac, audio/ogg, audio/wav, audio/aiff, audio/mp4 | | Video | video/mp4, video/quicktime, video/x-matroska, video/x-flv, video/x-msvideo | | Documents | application/pdf, application/zip, application/x-cfb, application/rtf | | Archives | application/x-rar-compressed, application/x-7z-compressed, application/gzip, | | application/x-bzip2, application/x-xz, application/zstd, application/x-lz4 | | Data | application/vnd.apache.parquet, application/vnd.apache.arrow.file | | Executables | application/x-elf, application/x-msdownload, application/x-mach-binary, | | application/wasm, application/vnd.android.dex | | Fonts | font/woff, font/woff2, font/otf, font/ttf | | Database | application/x-sqlite3 |

Summary

Types

error()

@type error() :: {:error, :unreadable | :unknown}

mime_type()

@type mime_type() :: String.t()

Functions

from_binary(data)

@spec from_binary(binary()) :: {:ok, mime_type()} | error()

Detects the MIME type from a binary.

Only the leading bytes are examined; passing the full file content is fine but unnecessary.

Examples

iex> MagicBytes.from_binary(<<0xFF, 0xD8, 0xFF, 0xE0>>)
{:ok, "image/jpeg"}

iex> MagicBytes.from_binary(<<0x89, "PNG", 0x0D, 0x0A, 0x1A, 0x0A>>)
{:ok, "image/png"}

iex> MagicBytes.from_binary(<<?%, ?P, ?D, ?F>>)
{:ok, "application/pdf"}

iex> MagicBytes.from_binary(<<0x1F, 0x8B>>)
{:ok, "application/gzip"}

iex> MagicBytes.from_binary(<<0x00, 0x00, 0x00, 0x00>>)
{:error, :unknown}

from_path(path)

@spec from_path(Path.t()) :: {:ok, mime_type()} | error()

Detects the MIME type of the file at path by reading its leading bytes.

Returns {:error, :unreadable} if the file cannot be opened.

Examples

iex> MagicBytes.from_path("test/fixtures/fixture.jpg")
{:ok, "image/jpeg"}

iex> MagicBytes.from_path("test/fixtures/fixture.png")
{:ok, "image/png"}

iex> MagicBytes.from_path("test/fixtures/fixture.pdf")
{:ok, "application/pdf"}

iex> MagicBytes.from_path("/nonexistent/file.jpg")
{:error, :unreadable}

from_stream(stream)

@spec from_stream(Enumerable.t()) :: {:ok, mime_type()} | error()

Detects the MIME type from a stream of binaries.

Chunks are accumulated until enough bytes are available, then detection runs on the combined header. The stream is not fully consumed.

Returns {:error, :unreadable} if the stream is empty.

Examples

iex> MagicBytes.from_stream([<<0xFF, 0xD8, 0xFF, 0xE0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>])
{:ok, "image/jpeg"}

iex> MagicBytes.from_stream([<<0xFF, 0xD8>>, <<0xFF, 0xE0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>])
{:ok, "image/jpeg"}

iex> MagicBytes.from_stream([])
{:error, :unreadable}

is_application_gzip(bin)

(macro)

is_application_pdf(bin)

(macro)

is_application_rtf(bin)

(macro)

is_application_vnd_android_dex(bin)

(macro)

is_application_vnd_apache_arrow_file(bin)

(macro)

is_application_vnd_apache_parquet(bin)

(macro)

is_application_wasm(bin)

(macro)

is_application_x_7z_compressed(bin)

(macro)

is_application_x_bzip2(bin)

(macro)

is_application_x_cfb(bin)

(macro)

is_application_x_elf(bin)

(macro)

is_application_x_lz4(bin)

(macro)

is_application_x_mach_binary(bin)

(macro)

is_application_x_msdownload(bin)

(macro)

is_application_x_rar_compressed(bin)

(macro)

is_application_x_sqlite3(bin)

(macro)

is_application_x_xz(bin)

(macro)

is_application_zip(bin)

(macro)

is_application_zstd(bin)

(macro)

is_audio_flac(bin)

(macro)

is_audio_mpeg(bin)

(macro)

is_audio_ogg(bin)

(macro)

is_font_otf(bin)

(macro)

is_font_ttf(bin)

(macro)

is_font_woff2(bin)

(macro)

is_font_woff(bin)

(macro)

is_image_bmp(bin)

(macro)

is_image_flif(bin)

(macro)

is_image_gif(bin)

(macro)

is_image_jp2(bin)

(macro)

is_image_jpeg(bin)

(macro)

is_image_jxl(bin)

(macro)

is_image_png(bin)

(macro)

is_image_tiff(bin)

(macro)

is_image_vnd_adobe_photoshop(bin)

(macro)

is_image_x_icon(bin)

(macro)

is_video_x_flv(bin)

(macro)

is_video_x_matroska(bin)

(macro)