Maak Your Own Bestandsoverdracht Service: Dev Tutorial
Bouw a bestand transfer service from scratch. Backend API design, storage integration, en user interface development tutorial.
De Autoriteit Persoonsgegevens (AP) behandelt bestandsoverdrachtsplatforms als gegevensverwerkende diensten: zodra persoonsgegevens via uw service worden verstuurd, gelden de verplichtingen van de AVG. De slimste architectuurbeslissing die u kunt nemen is ontwerpen zodat uw server uitsluitend ciphertekst ziet — dan heeft u per ontwerp zo min mogelijk te verwerken. U heeft vijf onderdelen nodig: een frontend die bestanden snijdt en optioneel in de browser versleutelt, een REST-API die uploadsessies aanmaakt en presigned opslag-URL's verstrekt, S3-compatibele objectopslag (Cloudflare R2, Backblaze B2 of MinIO), een kleine metadatadatabase (SQLite of Postgres) en een TLS 1.3-reverse-proxy. Met Node 22, Fastify en Vite bouwt u een WeTransfer-achtige dienst in circa 600 regels code en host u die voor vijf tot vijftien euro per maand bij gematigd verkeer.
Eerst de opslaglaag kiezen
Opslageconomie bepaalt het succes van een bestandsoverdrachtsservice. AWS S3 rekent 0,09 dollar per GB egress — een populaire download van 2 GB die 1.000 keer gedeeld wordt kost u 180 dollar aan bandbreedte. Cloudflare R2 rekent nul voor egress en 0,015 dollar per GB opgeslagen. Backblaze B2 rekent 0,01 dollar per GB egress en 0,006 dollar per GB opgeslagen. Voor een gratis transferservice is R2 de evidente keuze. MinIO stelt u in staat S3-semantiek zelf te hosten op eigen hardware, wat werkt als u al bandbreedte en schijven over heeft.
Gebruik presigned URL's zodat clients rechtstreeks bij de opslag uploaden en downloaden, nooit via uw server. Dit houdt uw API CPU-gebonden in plaats van bandbreedte-gebonden.
Het API-oppervlak schetsen
Houd de API klein. Vijf eindpunten dekken 95% van wat u nodig heeft:
POST /sessions— maak een uploadsessie aan, retourneer ID en presigned multipart-URL'sPOST /sessions/:id/complete— finaliseer een multipart-upload, retourneer deel-URLGET /shares/:id— retourneer bestandsmetadata (grootte, vervaldatum, resterende downloads)GET /shares/:id/download— retourneer een presigned download-URLDELETE /shares/:id— laat de eigenaar vroegtijdig intrekken (vereist een intrekkingstoken)
Begrens POST /sessions agressief — 20 per IP per uur is ruim voldoende. Alle eindpunten retourneren JSON, gebruiken Cache-Control: no-store en stellen Content-Security-Policy in op de HTML-pagina's.
De backend bedraden met Fastify
Fastify biedt JSON-schemavalidatie, verstandige logging en 30.000 verzoeken per seconde per kern zonder moeite. Een minimale POST /sessions:
fastify.post('/sessions', {
schema: {
body: {
type: 'object', required: ['size'],
properties: {
size: { type: 'integer', minimum: 1, maximum: 10737418240 },
contentType: { type: 'string', maxLength: 120 }
}
}
}
}, async (req) => {
const id = nanoid(16);
const key = `blobs/${id}`;
const upload = await s3.createMultipartUpload({
Bucket: 'transfers', Key: key, ContentType: req.body.contentType
});
const partCount = Math.ceil(req.body.size / (8 * 1024 * 1024));
const urls = await Promise.all(
Array.from({ length: partCount }, (_, i) =>
getSignedUrl(s3, new UploadPartCommand({
Bucket: 'transfers', Key: key, UploadId: upload.UploadId, PartNumber: i + 1
}), { expiresIn: 3600 }))
);
db.prepare('INSERT INTO sessions VALUES (?, ?, ?, ?)')
.run(id, upload.UploadId, key, Date.now());
return { id, uploadId: upload.UploadId, partUrls: urls };
});
Plafond 10 GB, onderdelen van 8 MB, URL-geldigheid één uur. Pas aan op basis van uw verkeersprofiel.
De upload-UI bouwen
Vite plus React plus nul UI-bibliotheken levert een werkende drag-and-drop in circa 150 regels. Het cruciale onderdeel is de upload-worker: start een Web Worker, geef het het File-handle en de deel-URL's, en laat het elk stuk PUT-ten met een gelijktijdigheid van drie. De hoofdthread blijft responsief; voortgangsupdates komen via postMessage.
// worker.js
self.onmessage = async ({ data: { file, partUrls } }) => {
const partSize = 8 * 1024 * 1024;
const etags = [];
for (let i = 0; i < partUrls.length; i++) {
const blob = file.slice(i * partSize, (i + 1) * partSize);
const res = await fetch(partUrls[i], { method: 'PUT', body: blob });
etags[i] = res.headers.get('etag');
self.postMessage({ type: 'progress', done: i + 1, total: partUrls.length });
}
self.postMessage({ type: 'done', etags });
};
Voeg een wachtwoordveld toe dat via PBKDF2 (600.000 iteraties, SHA-256) een sleutel afleidt en elk stuk door AES-256-GCM laat stromen voor het PUT-verzoek. Daarmee wordt uw service een end-to-end encryptie-dienst in plaats van een server-side-versleutelde dienst.
Het databaseschema ontwerpen
SQLite via better-sqlite3 verwerkt tientallen miljoenen rijen zonder moeite. Houd het schema slank:
CREATE TABLE sessions (
id TEXT PRIMARY KEY, upload_id TEXT, object_key TEXT, created_at INTEGER
);
CREATE TABLE shares (
id TEXT PRIMARY KEY, object_key TEXT, size_bytes INTEGER,
content_type TEXT, expires_at INTEGER, max_downloads INTEGER,
download_count INTEGER DEFAULT 0, revocation_token TEXT
);
CREATE INDEX idx_shares_expires ON shares(expires_at);
Geen gebruikerstabel, geen e-mail, geen IP-logs. Dit is een bewuste keuze — het maakt AVG-naleving bijna triviaal. Een nachtelijke cron verwijdert verlopen rijen en hun S3-objecten:
const expired = db.prepare('SELECT object_key FROM shares WHERE expires_at < ?').all(Date.now());
for (const { object_key } of expired) {
await s3.deleteObject({ Bucket: 'transfers', Key: object_key });
}
db.prepare('DELETE FROM shares WHERE expires_at < ?').run(Date.now());
Deel-links en downloads implementeren
Deel-URL's zien er bij client-side versleuteling uit als https://uwapp.nl/f/abc123#k=<base64sleutel>. Het fragment houdt de sleutel buiten serverlogs. De downloadpagina haalt /shares/abc123 op, krijgt een presigned S3-URL, streamt bytes rechtstreeks van R2, ontsleutelt in de browser en geeft het resultaat door aan StreamSaver.js of de File System Access API.
Stel Content-Disposition: attachment; filename*=UTF-8''encoded-naam in zodat de browser een opslagvenster toont in plaats van het bestand inline te tonen. Dit is van belang voor PDF-, HTML- en SVG-bestanden, die anders scripts kunnen uitvoeren in de browsercontext.
Beschermen tegen misbruik
Bestandsoverdrachtsservices trekken misbruikers aan. Verwacht drie categorieën: malwareverspreiding, phishing-payload-hosting en auteursrechtelijk beschermde uploads. Maatregelen naar impact gerangschikt: beperk uploads per IP, eis een minimumomvang (onder 10 KB is bijna altijd rommel), bereken een SHA-256 van de ciphertekst en check die tegen een denylist van bekende slechte hashes, en bied een laagdrempelig misbruikmeldingsformulier aan. ClamAV op ontsleutelde inhoud werkt alleen als u de sleutel heeft, wat end-to-end encryptie teniet doet — het juiste antwoord zijn inhoudsagnostische snelheidslimieten plus snelle afhandeling van meldingen.
Onderteken elke presigned URL met een korte TTL (maximaal één uur) en koppel sessies aan een door de browser gegenereerd tijdelijk token dat is opgeslagen in een Secure; HttpOnly; SameSite=Strict-cookie.
Goedkoop deployen en draaien
Een enkele Hetzner CX22 van tien euro (2 vCPU, 4 GB RAM) draait Fastify + SQLite + een Caddy-reverse-proxy en verwerkt duizenden actieve gebruikers. Caddy regelt TLS 1.3 automatisch via Let's Encrypt. Zet Cloudflare ervoor voor DDoS-bescherming en de gratis WAF. Monitorder met Uptime Kuma (gratis, zelf te hosten) en log naar stdout via journald.
Voor geografische redundantie repliceert u SQLite met Litestream naar R2 en wijst u een tweede regio naar dezelfde objectstore. Failover gebeurt op DNS-niveau.
De volledige aanpak hierboven weerspiegelt hoe HexaTransfer is gebouwd — Fastify-API, R2-opslag, Web Crypto in de browser, SQLite-metadata, geen accounts. Probeer het op https://hexatransfer.com — gratis, geen account vereist, maximaal 10 GB.
Wat u kunt overslaan tot u het nodig heeft
Bouw geen accounts, teams of mappen in versie 1. Voeg geen e-mailbezorging toe. Implementeer geen videopreview of afbeeldingsminiaturen (beide dwingen de server plaintekst te zien). Publiceer geen mobiele app voordat de webversie solide werkt op mobile Safari. De functiecreep die vroege bestandsoverdrachtsclones de das omdeed, volgde altijd hetzelfde patroon: accounts, opslagquota, betaling, teamplannen — en plotseling concurreert u met Dropbox in plaats van een snelle, privé transfertool te verzenden. Houd het oppervlak klein en de crypto strak.
Verstuur grote bestanden veilig met end-to-end-versleuteling
Draag bestanden tot 10 GB gratis over met end-to-end-versleuteling. Geen account nodig. Uw bestanden worden in uw browser versleuteld voordat ze worden geüpload — niemand anders kan ze lezen.
Een bestand verzenden