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\StorageInterfacewith a Flysystem-backed implementation.- Configure disks in
config('storage.disks.*')(e.g.,uploadsfor local,s3for 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(default3600seconds)- Optional CDN base:
S3_CDN_BASE_URL
- Mapping: these envs populate
config('storage.disks.s3.*')
Signed URLs
FileUploaderreturns 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_URLSflag 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_ttlas 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_SIZEandFILE_ALLOWED_EXTENSIONSin.env.
- Set
- Keep
S3_ACL=privatewithS3_SIGNED_URLS=truein 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(localors3). - You can explicitly pass a driver string to
new FileUploader(storageDriver: 's3')for one‑off cases.
- Prefer configuring via
- Blob persistence
FileUploaderwrites 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_sizeare sufficient. - S3 URL is signed instead of public: set
S3_ACL=public-readand/orS3_SIGNED_URLS=falseintentionally for public assets.