Concepts

File tree model

The file manager stores a hierarchical tree of file nodes on the assets disk, with content-addressed versions, optional chunked uploads, and async work for large duplicates and multi-file zips.

Related

Product UI: File manager. Config keys: Files & storage. HTTP: Admin & files API.

File node types

Enum: App\Enums\FileTypeEnum (app/Enums/FileTypeEnum.php).

CaseValueNotes
FilefileRegular uploaded blob (current UI create path)
FolderfolderContainer node (current UI create path)
LinklinkReserved in enum
ExternalexternalReserved in enum
GeneratedgeneratedReserved in enum
CollectioncollectionReserved in enum

Model: App\Models\File — hierarchical via parent_id / children, soft deletes, Spatie HasTags.

Current create paths

FileService / controller create flows and the TypeScript AdminFileRow.type currently use file and folder only. The other enum values exist for forward compatibility but are not wired into today’s create UI.

Versions

PieceDetail
ModelApp\Models\FileVersion
Tablefile_versions
Pointerfiles.current_version_idFile::currentVersion()
RelationFile::versions()
DedupFileService::resolveOrCreateVersion() reuses a version with the same content hash when possible

Version fields include disk, storage_path, hash, mime_type, size, width, height, and meta. Upload and replace flows update current_version_id to the resolved version.

Chunked uploads

Large uploads go through a session model rather than a single request body.

PieceDetail
ModelApp\Models\FileUpload
Tablefile_uploads
ServiceFileService::initChunkUpload / uploadChunk / complete-and-merge
Default diskassets
Session TTL24 hours from init
Cleanupfiles:cleanup-uploads (hourly schedule)

Routes (under can.manage.files in routes/admin.php):

MethodPath namePurpose
POSTfiles.uploads.initStart session
POSTfiles.uploads.chunkUpload a chunk
POSTfiles.uploads.completeMerge and create/update file
GETfiles.uploads.statusPoll session status

fileables / HasFiles

Polymorphic attachments let other models own files without owning the blob tree.

PiecePath / fact
TraitApp\Traits\HasFiles
Pivotfileables (file_id, fileable_id, fileable_type, role, order)
Trait APIfiles(), attachFile, detachFile, updateFile, syncFiles, filesByRole, firstFileByRole
HTTPPOST files/{file}/attach, POST files/{file}/detach
ValidationFileService attach/detach requires the target class to use HasFiles

Opt-in trait

No Eloquent model currently use HasFiles. The pivot, trait, and service endpoints are ready; domain models must opt in to become attach targets.

Tags

  • Spatie tags via HasTags on File
  • Sync: FileService::syncTags()
  • Route: PUT files/{file}/tags (files.tags)
  • Permission: can-tag-files
  • Middleware maps files.tags and bulk tag / untag actions in EnsureCanManageFiles

Favorites

  • Pivot: file_favorites (user_id, file_id)
  • Relations / service: File::favoritedBy(), FileService::favorite / unfavorite / bulkFavorite
  • Routes: POST / DELETE files/{file}/favorite
  • Permission: can-favorite-files
  • API resource exposes is_favorited

Disks (assets)

Configured in config/filesystems.php as disk assets:

SettingValue
Driverlocal
Rootstorage/app/public/assets
URL{FILE_PUBLIC_BASE}/storage/assets
Visibilitypublic

Default for files.disk, file_uploads.disk, and FileService create/upload paths.

Thumbnails — FileTransformService

Class: App\Services\FileTransformService.

BehaviorValue
Default size128 px
Max size256 px
Cleanup sizes64, 128, 256
Eligibletype = file, mime_type starts with image/, not SVG
FitContain-fit resize (GD)
FormatWebP q82 if available, else JPEG q82
Cache path{dirname(storage_path)}/transforms/{sha256}.webp|jpg on the same file disk
HTTPGET files/{file}/thumbnailfiles.thumbnail
Resourcethumbnail_url when isImage()

Zip and duplicate thresholds

From config/files.php:

Config keyEnvDefaultMeaning
files.duplicate_sync_max_bytesFILES_DUPLICATE_SYNC_MAX_BYTES52428800 (50 MiB)Single non-folder files at or below this size duplicate synchronously; folders, bulk selections, and larger files queue DuplicateFilesJob
files.zip_ttl_minutesFILES_ZIP_TTL_MINUTES60Prepared zips under storage/app/zips; cleanup / 410 after TTL (files:cleanup-zips)
files.zip_max_bytesFILES_ZIP_MAX_BYTES104857600 (100 MiB)Hard max archive size in FileService::buildZipArchive

Zip is always async for multi-download

Multi-file / folder download always queues PrepareFilesZipJob (HTTP 202). zip_max_bytes is a size cap, not a sync/async threshold. Duplicate sync vs async is the size-based split described above (FileController::shouldDuplicateSynchronously()).

Jobs and cleanup

Job / commandRole
DuplicateFilesJobAsync copy + notification
PrepareFilesZipJobBuild zip + notify download ready
files:cleanup-uploadsStale chunk sessions (hourly)
files:cleanup-zipsExpired zip archives (hourly)
Previous
Field types