media
Access to data about uploaded files and other media.
The medium (singular form of media) table stores all information of uploaded files or other media. Every resource can
contain a single medium. A resource with a medium is most often of the category image, audio, video or document.
In template the m_media model is used to fetch the medium record by the resource id: m.media[id]. This is the same
function as with m.rsc[id].medium except, that the m_rsc model does access control checks and the m_media does not.
The m_media model implements all functions to handle media files and is used by other Erlang modules.
Owned uploaded files and explicit previews use m_cafs for content-addressable
storage. filename and preview_filename remain logical filenames used by image
and media URLs; they need not exist as individual files in the archive directory.
Resolve files through Zotonic's file APIs instead of constructing local paths.
Identical bytes can share storage without sharing resource permissions.
The optional digest property is the lowercase hexadecimal SHA-256 digest of the
original bytes. Legacy media can lack a digest or CAFS mapping; existing files are
promoted on demand when their bytes are available. Reading a medium does not fetch
remote bytes to calculate a digest. See model#m_cafs for the storage lifecycle,
preview retention, and internal Erlang APIs.
Copy a medium to another resource
m_media:migrate_to_cafs(Context) queues a resumable scan of all current medium
records. It requires use mod_admin_config. Owned originals and extracted preview
images are deduplicated into CAFS; logical filenames remain unchanged. Local bytes
are hashed, while remote-only originals with a valid SHA-256 digest reuse their
existing remote location. Remote-only files without a digest are logged and skipped,
never downloaded for hashing. Retry tasks handle unavailable backends separately
from the scan. Legacy remote copies are retired only after a healthy canonical
remote copy exists; deletion follows filestore retention. Rerunning is safe.
m_media:delete_generated_previews(Context) queues background deletion of resized
variants and requires use permission on mod_admin_config. Original files and
preview_filename images are preserved, including CAFS bytes shared with them.
The maintenance page exposes this operation. Remote deletion follows filestore
retention; requests may regenerate previews during or after cleanup.
Use m_media:copy(Source, Destination, Context) to replace an existing resource's
medium with a copy of another resource's medium. Both arguments accept resource
ids or names. The result is ok or {error, Reason}.
m_media:copy(SourceId, DestinationId, Context).
The caller must be allowed to view the source, edit the destination, and insert
media of the source's MIME type and size. Permission failures return
{error, eacces}; missing resources or a source without a medium return
{error, enoent}. Copying a resource onto itself is a no-op after these checks.
A missing source medium never clears the destination.
The source is preserved. Destination resource properties, including category, title, and image-edit settings, are unchanged. Medium properties are copied with the destination id and a new creation timestamp. Owned originals and explicit previews receive independent logical filenames sharing CAFS bytes; mapped remote objects need no download. Legacy owned files are promoted on demand. Non-owned files, including library assets, are snapshotted into CAFS under new owned filenames without changing the source reference. Failure to prepare either owned file leaves the destination medium untouched and releases newly created aliases.
Replacement uses the normal medium notifications, cache invalidation, MQTT update,
and recovery retention for the destination's previous files. This is an Erlang API;
no HTTP or MQTT copy endpoint is provided. duplicate/3 remains the internal
insertion-only helper used when duplicating a whole resource.
Properties of a medium record
A medium record has minimally the following properties, other properties can be added by modules.
| Property | Description | Example value |
|---|---|---|
| id | Id of the medium record, equal to the page id. | 512 |
| filename | Logical filename of the uploaded file. For legacy storage, this is relative to the archive directory. | <<"2009/10/20/zotonic-datamodel.jpg">> |
| rootname | Root name of the filename. | <<"zotonic-datamodel">> |
| original_filename | Filename as suggested by the user agent when uploading the file. Directory paths are removed and unsafe characters replaced with underscores. The root name is limited to 80 Unicode characters; the extension is preserved. | <<"Zotonic-datamodel.jpg">> |
| mime | Mime type of the medium. | <<"image/jpeg">> |
| width | Width in pixels. | 536 |
| height | Height in pixels. | 737 |
| orientation | Exif oritentation of the image. | 1 |
| sha1 | Optional sha1 checksum of uploaded file. Undefined when not present. | |
| digest | Optional SHA-256 digest of the original bytes as 64 lowercase hexadecimal characters. Legacy media can lack this value. | |
| size | Size in bytes of the uploaded file. | 71585 |
| preview_filename | Optional filename for a generated file preview. | |
| preview_width | Optional. Width of the generated preview. | |
| preview_height | Optional. Height of the generated preview. | |
| is_deletable_file | If the file should be deleted when the medium record is deleted. A boolean. | true |
| is_deletable_preview | If the optionally generated preview file should be deleted when the medium record is deleted. A boolean. | false |
| created | Timestamp when the medium record is created. | {{2009,10,20},{13,47,27}} |
Available Model API Paths
| Method | Path pattern | Description |
|---|---|---|
get | /+id/... | Return medium record for resource +id when the resource is visible; returns enoent for unknown resource and eacces when not visible. |
post | /upload_check | Check payload digest (SHA-256), size, and filename for a visible current CAFS original. Returns needed: true on a miss, or needed: false plus media_upload_token. |
post | /+id | Replace medium of existing resource +id from payload url or uploaded file (#upload); optional payload rsc map updates resource props. No further lookups. |
post | / | Create a new resource+medium from payload url or uploaded file (#upload), with optional payload rsc properties for the new resource. No further lookups. |
delete | /+id | Delete medium linked to resource +id (queues file cleanup, emits media events); requires editable resource access. No further lookups. |
Upload clients can first call /upload_check. Unknown, inaccessible and unavailable
files return the same miss response. A successful token is signed, user-bound,
and valid for one hour. Pass file: {media_upload_token: Token} to the insert or
replace API, or use this map as a wired form field value. Source visibility and
its current CAFS mapping are rechecked at submission. Destination ACLs, scanning,
sanitization and media observers still run normally. Remote-only CAFS bytes can
be fetched server-side for this processing. Visible legacy originals with a
matching checksum are lazily promoted before reuse. Originals without a known checksum and files changed by previous
conversion require a normal upload.
/+name marks a variable path segment. A trailing /... means extra path segments are accepted for further lookups.