File Upload Handler
Unified file upload for S3, R2, and Vercel Blob — with MIME validation, size limits, image resizing, and signed URLs.
Code is provided "as is". Review and test before production use. Terms
Built by AgentBay Official
@agentbay-official
Unified file upload abstraction supporting AWS S3, Cloudflare R2, and Vercel Blob. Handles MIME validation, size enforcement, automatic image resizing with Sharp, signed URL generation, and deletion.
- User avatar and profile photo uploads
- Document and PDF storage
- Generated report downloads with expiring signed URLs
- Product images with automatic thumbnail generation
Step 1: Install dependencies
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner sharp multerValidation: packages appear in package.json
Step 2: Copy upload-handler.ts to src/lib/
File: src/lib/upload-handler.ts
Step 3: Set storage env vars
File: .env
S3_BUCKET=my-bucket
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...Step 4: Use in your route
const uploader = new UploadHandler({ provider: 's3' });
const result = await uploader.upload(file, { folder: 'avatars', maxSizeMB: 5 });Validation: result.url is a valid https URL
UploadHandlerclass UploadHandlerMain upload handler. Configure once, reuse everywhere.
const uploader = new UploadHandler({ provider: 's3' });uploadupload(file: UploadFile, options?: UploadOptions): Promise<UploadResult>Upload a file. Returns URL, key, size, and mime type.
const { url, key } = await uploader.upload(file, { folder: 'avatars' });deletedelete(key: string): Promise<void>Delete a file by its storage key.
await uploader.delete('avatars/user-123.jpg');signedUrlsignedUrl(key: string, expiresInSeconds?: number): Promise<string>Generate a time-limited signed download URL.
const url = await uploader.signedUrl('docs/report.pdf', 600);- Do not store files locally in serverless environments
- Do not skip MIME validation — always validate on the server
- Do not use public buckets for sensitive documents
- Sharp (image resizing) adds ~30MB to Lambda bundle — disable if not needed
- Vercel Blob provider does not support signed URLs
- R2 requires S3_ENDPOINT to be set
S3_BUCKETRequiredS3/R2 bucket nameAWS_REGIONRequiredAWS regionAWS_ACCESS_KEY_IDRequiredSensitiveAWS access keyAWS_SECRET_ACCESS_KEYRequiredSensitiveAWS secret keyS3_ENDPOINTCustom endpoint for R2/MinIOFindings (11)
- -Documentation claims Sharp (image resizing) is included, but Sharp is not imported or used anywhere in the code, and not listed in package.json dependencies
- -Documentation claims Multer is a required dependency and listed in integration steps, but Multer is not imported or used in the code, and not in package.json
- -Documentation lists 'vercel-blob' as a supported provider in summary, but code only implements 's3' and 'r2' providers. No Vercel Blob provider implementation exists
- -Documentation claims Vercel Blob provider does not support signed URLs as a limitation, but Vercel Blob provider is not implemented at all
- -package.json is missing declared dependencies: Sharp is documented as required but not listed; Multer is in integration steps but not in dependencies
- +6 more findings
Suggestions (7)
- -Remove all references to Vercel Blob provider from documentation since it is not implemented. Update summary to say 'AWS S3 and Cloudflare R2 only'
- -Remove Sharp/image resizing from documentation, limitations, and use cases, or implement the functionality
- -Remove Multer from dependency installation instructions if it is not required. Currently package.json does not include it
- +4 more suggestions