MagicBytes

CIHex VersionHex DownloadsLicense

Detects MIME types from binary content using magic byte signatures. Only the leading bytes of a file are examined, making detection fast regardless of file size.

Installation

def deps do
  [
    {:magic_bytes, "~> 0.2"}
  ]
end

Usage

From a file path

MagicBytes.from_path("image.png")
#=> {:ok, "image/png"}

MagicBytes.from_path("archive.tar.gz")
#=> {:ok, "application/gzip"}

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

From a binary

Useful when bytes are already in memory — e.g. an upload buffer or a database blob. Only the leading bytes matter; passing the full content works but is not required.

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

MagicBytes.from_binary(file_contents)
#=> {:ok, "application/pdf"}

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

From a stream

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

File.stream!("video.mkv", 1024)
|> MagicBytes.from_stream()
#=> {:ok, "video/x-matroska"}

Guards

For prefix-based signatures a corresponding guard macro is generated and exported from MagicBytes. Guard names follow the pattern is_<mime_type> with / and - replaced by _.

require MagicBytes

def process(bin) when MagicBytes.is_image_jpeg(bin), do: ...
def process(bin) when MagicBytes.is_image_png(bin), do: ...
def process(bin) when MagicBytes.is_application_pdf(bin), do: ...
def process(_bin), do: {:error, :unsupported}

Guards also work as boolean expressions outside when clauses:

require MagicBytes
MagicBytes.is_application_gzip(data)  #=> true | false

Guards are not generated for container-format signatures where the distinguishing bytes appear beyond a fixed prefix (WebP, WAV, AVI, AIFF, MP4, HEIC, AVIF, QuickTime). Use from_binary/1 for those formats.

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

  # Prefix-based: magic bytes at offset 0
  defsignature("application/x-cld", <<0xCA, 0xFE, 0xD0, 0x0D>>)

  # Offset-based: magic bytes at a specific byte offset
  defsignature_at("application/x-tar", 257, "ustar")
end
# config/config.exs
config :magic_bytes,
  extra_signatures: MyApp.Signatures,
  # Required when using offset-based signatures — set to offset + byte_size(magic)
  read_bytes: 262
MagicBytes.from_binary(data)
#=> {:ok, "application/x-cld"}  # or any built-in type

Passing guards: true generates guard macros on your module for both prefix and offset signatures. Because your module compiles after the magic_bytes dependency, guards live on your module rather than on MagicBytes:

require MyApp.Signatures

def process(bin) when MyApp.Signatures.is_application_x_cld(bin), do: ...
def process(bin) when MyApp.Signatures.is_application_x_tar(bin), do: ...

ZIP-based formats

Formats that are ZIP files internally (.docx, .xlsx, .odt, etc.) are correctly detected as application/zip. Distinguishing between them requires parsing the ZIP structure, which is outside the scope of this library. Pair with a ZIP parser for sub-format detection if needed.

Configuration

All options are resolved at compile time via Application.compile_env.

Key Type Default Description
:extra_signatures module nil Module with additional signatures defined via use MagicBytes.DefineSignatures
:read_bytes pos_integer auto Bytes read from input. Defaults to the minimum required by the built-in signatures. Set explicitly when using offset-based custom signatures.
:only list(string) nil When set, only these MIME types are returned; all others become {:error, :unknown}
:exclude list(string) [] MIME types to suppress. Ignored when :only is set.

Supported formats

Category MIME types
Images image/jpegimage/pngimage/gifimage/webpimage/bmpimage/tiffimage/x-iconimage/vnd.adobe.photoshopimage/heicimage/avifimage/jp2image/jxlimage/flif
Audio audio/mpegaudio/flacaudio/oggaudio/wavaudio/aiffaudio/mp4
Video video/mp4video/quicktimevideo/x-matroskavideo/x-flvvideo/x-msvideo
Documents application/pdfapplication/zipapplication/x-cfbapplication/rtf
Archives application/x-rar-compressedapplication/x-7z-compressedapplication/gzipapplication/x-bzip2application/x-xzapplication/zstdapplication/x-lz4
Data application/vnd.apache.parquetapplication/vnd.apache.arrow.file
Executables application/x-elfapplication/x-msdownloadapplication/x-mach-binaryapplication/wasmapplication/vnd.android.dex
Fonts font/wofffont/woff2font/otffont/ttf
Database application/x-sqlite3