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.

PropertyDescriptionExample value
idId of the medium record, equal to the page id.512
filenameLogical filename of the uploaded file. For legacy storage, this is relative to the archive directory.<<"2009/10/20/zotonic-datamodel.jpg">>
rootnameRoot name of the filename.<<"zotonic-datamodel">>
original_filenameFilename 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">>
mimeMime type of the medium.<<"image/jpeg">>
widthWidth in pixels.536
heightHeight in pixels.737
orientationExif oritentation of the image.1
sha1Optional sha1 checksum of uploaded file. Undefined when not present.
digestOptional SHA-256 digest of the original bytes as 64 lowercase hexadecimal characters. Legacy media can lack this value.
sizeSize in bytes of the uploaded file.71585
preview_filenameOptional filename for a generated file preview.
preview_widthOptional. Width of the generated preview.
preview_heightOptional. Height of the generated preview.
is_deletable_fileIf the file should be deleted when the medium record is deleted. A boolean.true
is_deletable_previewIf the optionally generated preview file should be deleted when the medium record is deleted. A boolean.false
createdTimestamp when the medium record is created.{{2009,10,20},{13,47,27}}

Available Model API Paths

MethodPath patternDescription
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_checkCheck 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/+idReplace 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/+idDelete 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.

Edit on GitHub

Referred by

Developer guide

Render and manage media

Use resource IDs, media metadata and consistent responsive-image descriptors.