Cookbook
File Uploads (Local and S3)
This guide shows how to accept uploads securely, store them locally or on S3, and return URLs to clients using the built‑in Glueful\Uploader
utilities.
Overview
- Uploader class:
Glueful\Uploader\FileUploader
- Storage via
Glueful\Uploader\Storage\StorageInterface
with a Flysystem-backed implementation.- Configure disks in
config('storage.disks.*')
(e.g.,uploads
for local,s3
for Amazon S3) - Select the default disk with
config('storage.default')
or envSTORAGE_DEFAULT_DISK
/STORAGE_DRIVER
- Configure disks in
- Safety: max file size, extension + MIME validation, basic hazard scanning, checksum utility
- Utilities: directory statistics and cleanup
Configuration
Local paths
- Upload root:
config('app.paths.uploads')
- CDN base URL:
config('app.urls.cdn')
File limits and validation
- Max size:
config('filesystem.security.max_upload_size')
- Env:
MAX_FILE_UPLOAD_SIZE
(bytes). Example:10485760
(10MB)
- Env:
- Allowed extensions:
config('filesystem.file_manager.allowed_extensions')
- Env:
FILE_ALLOWED_EXTENSIONS="jpg,jpeg,png,gif,pdf,doc,docx,txt"
- Env:
- Hazard scanning:
config('filesystem.security.scan_uploads', true)
- Looks for code markers in the first 64KB (basic safeguard)
Amazon S3
- Select backend: set
STORAGE_DRIVER=s3
(orSTORAGE_DEFAULT_DISK=s3
) - Required env:
S3_ACCESS_KEY_ID
,S3_SECRET_ACCESS_KEY
,S3_REGION
,S3_BUCKET
- Optional:
S3_ENDPOINT
(for S3‑compatible hosts)
- ACL and URL behavior:
S3_ACL
(defaultprivate
)S3_SIGNED_URLS
(defaulttrue
)S3_SIGNED_URL_TTL
(default3600
seconds)- Optional CDN base:
S3_CDN_BASE_URL
- Mapping: these envs populate
config('storage.disks.s3.*')
Signed URLs
FileUploader
returns a plain/public URL via the UrlGenerator (base_url
/cdn_base_url
) when you callhandleUpload(...)
.- To obtain a time‑limited, signed URL on S3, use the storage adapter’s
getSignedUrl($path, $ttl)
method (available onStorageInterface
). Example:
use Glueful\Uploader\Storage\FlysystemStorage;
use Glueful\Storage\{StorageManager};
use Glueful\Storage\Support\UrlGenerator;
// Resolve shared services from the container and target the 's3' disk
$storage = new FlysystemStorage(
app(StorageManager::class),
app(UrlGenerator::class),
's3'
);
// $result['url'] is the path returned from handleUpload()
$signed = $storage->getSignedUrl($result['url'], 600); // 10 minutes
Notes:
- The
S3_SIGNED_URLS
flag is not auto‑enforced byFileUploader
; use it as an application toggle to decide when to callgetSignedUrl()
. - When no expiry is provided, the adapter uses
storage.disks.s3.signed_ttl
as a default.
Handling browser form uploads
use Glueful\Uploader\FileUploader;
use Symfony\Component\HttpFoundation\Request;
class UploadController
{
public function store(Request $request)
{
// Acquire uploader (via container or direct instantiation)
$uploader = new FileUploader();
// Token + params are application‑defined (example shows query params)
$token = (string) $request->headers->get('X-Upload-Token', '');
// Include required params for uploader validation
// Ensure a 'user_id' is present in params
$getParams = array_merge($request->query->all(), [
'user_id' => (string) ($request->headers->get('X-User-Id') ?? ''),
]);
// For simplicity, pass native PHP files array if available
// If using Symfony UploadedFile objects, convert to arrays (see below)
$fileParams = $_FILES;
// If your files are nested (e.g., `files[avatar]`), pass a key selector:
// $getParams['key'] = 'avatar';
$result = $uploader->handleUpload($token, $getParams, $fileParams);
if (isset($result['error'])) {
return response()->json($result, $result['code'] ?? 400);
}
return response()->json([
'uuid' => $result['uuid'],
'url' => $result['url'],
]);
}
}
Symfony UploadedFile
support: you can pass either native $_FILES
arrays or UploadedFile
instances directly; the uploader normalizes them internally.
Base64 uploads
When clients send base64 strings (e.g., mobile apps):
$base64 = (string) $request->request->get('file_base64', '');
$tempPath = $uploader->handleBase64Upload($base64);
// Build a synthetic file array for the same flow
$mime = mime_content_type($tempPath) ?: 'application/octet-stream';
$synthetic = [
'name' => 'upload.' . ($mime === 'image/png' ? 'png' : 'bin'),
'type' => $mime,
'tmp_name' => $tempPath,
'error' => UPLOAD_ERR_OK,
'size' => filesize($tempPath) ?: 0,
];
$result = $uploader->handleUpload($token, $getParams, $synthetic);
Note: handleBase64Upload
writes to a temporary file and returns its path. The example above passes it through the same handleUpload
pipeline so storage/validation logic is reused.
Directory utilities
- Stats (count, size by type):
$stats = $uploader->getDirectoryStats(config('app.paths.uploads'));
// ['exists' => true,
// 'total_files' => 42,
// 'total_size' => 123456,
// 'total_size_human' => '120.56 KB',
// 'file_types' => [...],
// 'directory' => '/path/to/uploads']
- Cleanup old files (age in seconds):
$cleanup = $uploader->cleanupOldFiles(config('app.paths.uploads'), 86400); // 24h
// ['deleted_files' => N, 'freed_space' => bytes, 'freed_space_human' => '...']
Security notes
- Enforce limits and lists:
- Set
MAX_FILE_UPLOAD_SIZE
andFILE_ALLOWED_EXTENSIONS
in.env
.
- Set
- Keep
S3_ACL=private
withS3_SIGNED_URLS=true
in production unless public distribution is intentional. - Consider virus scanning hooks for stricter environments (the built‑in scan is a lightweight heuristic).
Checksums
Compute a SHA‑256 checksum for a file to verify integrity or deduplicate uploads:
$checksum = $uploader->calculateChecksum($filePath); // hex string
Customization
- Storage driver
- Prefer configuring via
STORAGE_DRIVER
(local
ors3
). - You can explicitly pass a driver string to
new FileUploader(storageDriver: 's3')
for one‑off cases.
- Prefer configuring via
- Blob persistence
FileUploader
writes a record viaGlueful\Repository\BlobRepository
(resolved from the container). You can override or extend this repository in your application container.
- Filenames
- Filenames are randomly generated with a timestamp; when no extension is provided, the uploader derives one from the detected MIME where possible.
Troubleshooting
- “Invalid file type”: ensure extension and MIME are allowed (both are validated).
- “File size exceeds limit”: increase
MAX_FILE_UPLOAD_SIZE
(bytes) and confirm PHPupload_max_filesize
/post_max_size
are sufficient. - S3 URL is signed instead of public: set
S3_ACL=public-read
and/orS3_SIGNED_URLS=false
intentionally for public assets.