Files
Files live in the page folder, stay private until you ask for their URL, and are managed through PageFiles.
On this page
Where files live
A file belongs to a page and sits in its folder, beside the content:
site/pages/blog/my-post/
_index.json
_data.json
cover.jpg
_cover.jpg.json
_cover.jpg.json holds the file's metadata: alt, mime and created. A file's identity is its page's UUID plus its filename; there is no separate file UUID.
Private until you ask for the URL
A file in a page folder is not served just because it exists. The first call to url() (or src() for an image variant) exposes that URL, and only an exposed URL is served. A request for a private file 404s even though it is on disk.
Each URL is exposed on its own: src('w=200') exposes that variant, not the original at /{page-slug}/cover.jpg; call url() for that. Exposing a URL is not content, so rendering a page never writes to the page folder or to History.
Make private in a file's admin menu withdraws its URLs, and so does deleting it.
Originals are streamed by PHP. Resized variants of a page open to anyone are generated on first request into site/public/, which is managed by the CMS: everything in it except index.php is generated and safe to delete. A variant of a gated or unpublished page is never written there. It is kept in site/data/cache/files/ and sent through PHP, behind the page's access check. phlat files:rebuild sweeps out whatever is stale.
In templates
Page::file() and Page::image() resolve a filename against the page's own folder:
{var $pdf = $page->file('brochure.pdf')}
<a href="{$pdf->url()}">Download ({$pdf->size|bytes})</a>
<img src="{$page->image('cover.jpg')->src('w=800')}" alt="">
image() throws for files that aren't resizable images, SVG and ICO included; use file() for those. Transforms are in Images.
$page->files() returns every file on the page as PageFile objects:
{foreach $page->files() as $file}
<a href="{$file->url()}">{$file->basename}</a>
{/foreach}
A PageFile has url(), isPublic(), filename, mime, size, extension and type (image, video, audio, document, archive, code or file). url() adds a cache-busting query string; pass false to leave it off.
File and image fields
A file or image field stores a bare filename and resolves it against its own page. {$page->cover} outputs the public URL, and $page->cover->decode() returns the PageFile or PageImage. Uploads are handled by the admin editor.
Uploading
$file = $page->upload($_FILES['file']);
Page::upload() rejects failed uploads and extensions not in media.allowed, cleans the filename and stores the file in the page folder. The default list is jpg, jpeg, png, gif, webp, svg, pdf, mp4, webm, mp3 and zip.
PageFiles
$app->pageFiles() is the layer underneath, for code that doesn't start from a page:
| Method | Does |
|---|---|
store($sourcePath, $name, $pageUuid, $options) |
Copy a local file into a page |
upload($tmpPath, $name, $pageUuid, $options) |
Store an uploaded file |
get($pageUuid, $filename) |
The PageFile, or null if it has no metadata |
update($pageUuid, $filename, $changes) |
Change metadata, such as alt |
delete($pageUuid, $filename) |
Remove the file, its metadata and its variants |
forPage($pageUuid) |
Every file on a page |
isPublic($pageUuid, $filename) |
Whether the file, or a variant of it, has been exposed |
makePrivate($pageUuid, $filename) |
Withdraw its URLs and wipe its mirrors and cached variants |
rebuild() |
Sweep stale variants out of site/public/ |
Storing or deleting a file clears its cached variants and public mirror.
Image variants
Resized images are generated on first request and regenerated when the source changes. When the page is open to anyone, the variant is written into site/public/ and the web server serves it as a static file after that. Otherwise it is kept in site/data/cache/files/, made again when missing, and served through PHP behind the page's access check. The admin's thumbnails live there too. See Images.
Caching
Public files are sent with files.cache as Cache-Control (default public, max-age=3600). A file larger than files.cache_max_bytes (default 5 MB) is sent no-store.
When you need more
To react when a file is stored or deleted, see Event Hooks.