Behaviour and default implementation for content-based MIME type detection.
PhxMediaLibrary uses this module to detect MIME types from file content
(magic bytes) rather than relying solely on file extensions. This provides
a security layer against files with mismatched extensions (e.g. an executable
renamed to .jpg).
How It Works
- The detector reads the first bytes of the file content
- It matches against known magic byte signatures
- If a match is found, that becomes the detected MIME type
- If no match is found, it falls back to extension-based detection
Custom Implementation
You can provide your own detector by implementing the behaviour and configuring it:
defmodule MyApp.MimeDetector do
@behaviour PhxMediaLibrary.MimeDetector
@impl true
def detect(content, filename) do
# Your custom detection logic
{:ok, "application/octet-stream"}
end
end
config :phx_media_library,
mime_detector: MyApp.MimeDetectorConfiguration
The default detector can be configured to skip verification:
# Disable content-type verification globally
config :phx_media_library,
verify_content_type: falseOr per-collection:
collection(:uploads, verify_content_type: false)
Summary
Types
Result of MIME type detection.
Callbacks
Detect the MIME type of file content.
Functions
Detect the MIME type from binary content using magic byte signatures.
Detect the MIME type from content, falling back to extension-based detection.
Detect the MIME type and verify it matches the declared type.
Types
@type detection_result() :: {:ok, String.t()} | {:error, :unrecognized}
Result of MIME type detection.
{:ok, mime_type}— a MIME type was successfully detected{:error, :unrecognized}— the content didn't match any known signature
Callbacks
@callback detect(content :: binary(), filename :: String.t()) :: detection_result()
Detect the MIME type of file content.
Receives the raw binary content (or at least the first few KB) and the original filename. Implementations should primarily use the content for detection and may use the filename as a fallback.
Returns {:ok, mime_type} if a type was detected, or
{:error, :unrecognized} if the content didn't match any known signature.
Functions
@spec detect(binary(), String.t()) :: detection_result()
Detect the MIME type from binary content using magic byte signatures.
Uses the configured detector module, falling back to the built-in magic bytes implementation.
Examples
iex> png_header = <<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, "rest of file">>
iex> PhxMediaLibrary.MimeDetector.detect(png_header, "photo.png")
{:ok, "image/png"}
iex> PhxMediaLibrary.MimeDetector.detect("just some text", "notes.txt")
{:error, :unrecognized}
Detect the MIME type from content, falling back to extension-based detection.
Unlike detect/2, this always returns a MIME type string — it falls back
to MIME.from_path/1 when magic bytes don't match.
Examples
iex> png_header = <<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, "rest">>
iex> PhxMediaLibrary.MimeDetector.detect_with_fallback(png_header, "photo.png")
"image/png"
iex> PhxMediaLibrary.MimeDetector.detect_with_fallback("unknown content", "data.csv")
"text/csv"
@spec verify(binary(), String.t(), String.t()) :: :ok | {:error, {:content_type_mismatch, String.t(), String.t()}}
Detect the MIME type and verify it matches the declared type.
Returns :ok if the detected type matches or if detection is inconclusive
(falls back to trusting the declared type). Returns
{:error, {:content_type_mismatch, detected, declared}} if the content
clearly doesn't match the declared type.
Parameters
content— the raw binary file content (or its first few KB)filename— the original filenamedeclared_mime— the MIME type declared by the file extension or upload metadata
Examples
iex> png_header = <<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, "rest">>
iex> PhxMediaLibrary.MimeDetector.verify(png_header, "photo.png", "image/png")
:ok
iex> png_header = <<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, "rest">>
iex> PhxMediaLibrary.MimeDetector.verify(png_header, "fake.exe", "application/x-msdownload")
{:error, {:content_type_mismatch, "image/png", "application/x-msdownload"}}