Media and file delivery system (in-house CDN)
A standalone service for images, videos and files. It takes over uploading, processing and serving from the platform, so the main system does not have to deal with large content.
I designed and built it, starting in March 2026.
An events platform carries a huge amount of images and video: club logos, covers, galleries, stories, promo videos. If the main system stores and serves them, the site slows down, and large uploads often break.
I wanted a separate, secure service that optimises content, serves it quickly, reliably accepts large videos, and works through a simple API.
Images, automatically
On upload, WebP conversion and thumbnails are produced, with per-profile sizes and quality (logo, cover, gallery, story and more).
Video streaming
In the background it builds an adaptive stream (480p, 720p, 1080p), an easily embeddable MP4, a thumbnail and a poster image, with volume normalisation.
Upload pages
One-time, time-limited upload links with drag-and-drop and a progress bar, for images, video and any file.
Large files without breaking
Uploads go in chunks with per-chunk retries, so even a gigabyte-sized video arrives reliably.
Private and public content
Private files are only reachable through signed, time-limited addresses, while public ones can be served directly.
API and feedback
Key-based access with per-key permissions, a documented API, status queries and a callback (webhook) when processing ends.
Endpoints for images, files and upload sessions, with key-based access and an OpenAPI description.
With brand and customer names and personal data removed (except the app store screenshots).
Adaptive video
480p, 720p and 1080p streams that only scale down, and work for both portrait and landscape video. A plain MP4 is made too, for anyone who just wants to embed it.
One-time upload links
Uploads go through a link valid for 30 minutes, with drag-and-drop and a progress bar.
Private files with signed links
A private file is only reachable through a signed, time-limited address, 15 minutes by default.
The layer in between killed large uploads
The upload went in a single request, but the network layer in between cut it off at around 100 MB and 100 seconds. Now I upload in 20 MB chunks, and the server assembles them in the last step and checks the size. Resending a chunk is safe, so the browser retries four times, with longer and longer waits.
An upload must not be able to do harm
I blocked executable and server-side extensions (exe, php, js and the rest). I check type and size after assembly, and there is a limit on video length.
Do not lose the failure
Video processing runs in the background. On success and on failure it sends a callback to the operating system, retrying five times, and if processing fails, an alert goes out.
Chunked upload
20 MB chunks that the server assembles and verifies in the final step. Resending a chunk is safe. This was needed because an intermediate network layer limits request size and duration, which broke large video uploads.
Background processing chain
Video processing runs on a message queue in several steps, with alerts on failure. The callback arrives on success and on failure, with retries at increasing intervals.
Security
Blocking executable and server-side file types, type and size checks, API-key permissions, signed URLs and a maximum video length.
Storage management
Hourly cleanup of expired upload sessions, and optionally deleting the originals of processed videos after a set time to save storage.
Upload that survives interruptions
Parts arrive by index, and resending the same index overwrites the part, so a retry is harmless. After assembly it verifies the size and cleans up on failure.
// Re-sending the same index overwrites the part, so a retry is idempotent.
$request->file('chunk')->move($dir, sprintf('%06d.part', $index));
// ... later, in complete(): assemble the parts in order
$out = fopen($assembled, 'wb');
for ($i = 0; $i < $info['total_chunks']; $i++) {
$part = $dir.'/'.sprintf('%06d.part', $i);
if (! is_file($part)) {
fclose($out);
return response()->json(['success' => false, 'message' => "Missing chunk: {$i}"], 422);
}
$in = fopen($part, 'rb');
stream_copy_to_stream($in, $out);
fclose($in);
}
fclose($out);
if (filesize($assembled) !== $info['size']) {
File::deleteDirectory($dir);
return response()->json(['success' => false, 'message' => 'Size mismatch.'], 422);
}
The platform and the admin manage content through one API, the heavy processing runs in the background, and large files arrive without breaking.
Are images and videos slowing your system down?
If uploads break, or the site is slow because of large content, it is worth a look.
More projects
Dental practice management
Appointments, treatment records, cash desk, invoicing and regulatory reports in one system, for multiple practices.
Case study → Web shopCar and outdoor web shop
An exact-fit product finder, promotions and discount codes, with over 15,000 orders served.
Case study → Web application and APITicketing platform
From ticket purchase to entry control and follow-up: a web system with payments, invoicing, a mobile API and a community layer.
Case study →