File Upload Implementation: Validation, Progress Bar, Security

You launched an MVP in a month, and the very first client file of 200 MB brought the server down — timeout, 502, user furious. Sound familiar? The issue isn't hardware but the lack of proper file upload implementation: no chunked upload, no validation on client and server, no progress bar. We've tun

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1414
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    982
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    995

You launched an MVP in a month, and the very first client file of 200 MB brought the server down — timeout, 502, user furious. Sound familiar? The issue isn't hardware but the lack of proper file upload implementation: no chunked upload, no validation on client and server, no progress bar. We've tuned such cases dozens of times and repeatedly hit the same pitfalls. This article will explain how to do upload correctly — with validation, progress, security, and support for large files.

What Problems We Solve

Unexpected upload timeout. File 200 MB, Nginx configured for 30 seconds — the server kills the connection, user reloads the page. Solution: chunked upload (splitting into parts) or adjusting client_max_body_size and fastcgi_read_timeout, but that's a band-aid. Chunked upload always works.

Validation only on the client. Any schoolkid can send a POST with curl and upload a .exe instead of .jpg. On the server, we check MIME via finfo, not trusting the header. We limit size, number of files, and verify the signature.

Loss of progress. The user doesn't see how long to wait and closes the tab. We add a progress bar via onUploadProgress (Axios) or XMLHttpRequest. For chunked — we show parts. Saving on rework and reducing support tickets — that's what proper file upload gives.

How We Do It: Stack and Implementation

We use Laravel 11 (PHP 8.3) + S3 (MinIO or AWS) + React 18 (TypeScript). For large files — multipart upload via S3 SDK. Below is code that works in production.

Server: Laravel

class FileUploadController extends Controller { public function store(Request $request): JsonResponse { $request->validate([ 'file' => [ 'required', 'file', 'max:51200', // 50 MB in KB 'mimes:jpg,jpeg,png,gif,webp,pdf,docx,xlsx,zip', ], ]); $file = $request->file('file'); // Generate safe name — do not use original name $filename = Str::uuid() . '.' . $file->getClientOriginalExtension(); $path = 'uploads/' . auth()->id() . '/' . date('Y/m') . '/' . $filename; // Upload to S3 Storage::disk('s3')->putFileAs( dirname($path), $file, basename($path), ['visibility' => 'private'] ); $upload = Upload::create([ 'user_id' => auth()->id(), 'path' => $path, 'original_name' => $file->getClientOriginalName(), 'mime_type' => $file->getMimeType(), 'size' => $file->getSize(), ]); return response()->json(['id' => $upload->id, 'path' => $path], 201); } } 

Client: React with Progress Bar

function FileUploader() { const [progress, setProgress] = useState(0); const [uploading, setUploading] = useState(false); async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) { const file = e.target.files?.[0]; if (!file) return; const formData = new FormData(); formData.append('file', file); setUploading(true); try { await axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (e) => { setProgress(Math.round((e.loaded / (e.total ?? 1)) * 100)); }, }); } finally { setUploading(false); } } return ( <div> <input type="file" onChange={handleUpload} disabled={uploading} /> {uploading && <progress value={progress} max={100}>{progress}%</progress>} </div> ); } 

Chunked Upload for Large Files

Files >100 MB are uploaded in parts via S3 Multipart Upload:

// Initiation public function initChunked(Request $request): JsonResponse { $s3 = Storage::disk('s3')->getClient(); $result = $s3->createMultipartUpload([ 'Bucket' => config('filesystems.disks.s3.bucket'), 'Key' => 'uploads/' . Str::uuid() . '.' . $request->extension, ]); return response()->json(['upload_id' => $result['UploadId'], 'key' => $result['Key']]); } // Upload part public function uploadPart(Request $request): JsonResponse { $s3 = Storage::disk('s3')->getClient(); $result = $s3->uploadPart([ 'Bucket' => config('filesystems.disks.s3.bucket'), 'Key' => $request->key, 'UploadId' => $request->upload_id, 'PartNumber' => $request->part_number, 'Body' => $request->getContent(), ]); return response()->json(['etag' => $result['ETag']]); } 

Approach Comparison: Regular Upload vs Chunked

Parameter Regular Upload Chunked Upload
Timeout High (>50 MB) Low (each part is small)
Progress Simple (one request) Detailed (by parts)
Resume No Yes (from interrupted part)
Complexity Low Medium (S3 SDK required)
Best for Files < 50 MB Files > 50 MB

Additionally: chunked upload reduces timeouts by 80% according to our data, which is critical for user experience. The cost of implementation pays off through reduced support load.

Why Choose Chunked Upload?

Let's break down two approaches: regular upload vs chunked. Regular is simpler to implement, but on files >100 MB it gives a large number of timeouts (80% of cases according to our data). Chunked upload solves the problem but requires S3 setup and additional endpoints. We use the second option for all projects where large file upload is expected. It's justified: the user doesn't lose data, doesn't reload the page, and the upload progress keeps them informed.

How to Avoid Common Mistakes?

Checklist: what to verify before deployment:

  • Forgot the Nginx limit. client_max_body_size must be larger than your max. Otherwise 413.
  • Original file name. Never save as-is — use UUID.
  • Only one check. Validation on client + server is mandatory.
  • Cleanup not configured. If user started upload but didn't finish, parts linger in S3. A daily cron job removes "stuck" parts.

Work Stages and Estimated Timeline

Stage Duration
Analysis (file types, sizes, location) 1 day
Design and storage selection (S3 vs local) 0.5 day
Implementation of controllers, validation, client code 1–2 days
Testing (various sizes, errors, timeouts) 1 day
Deployment and S3 setup, monitoring 0.5 day

Total: 3–5 days depending on complexity.

What's Included

  • API endpoint documentation and request formats.
  • Access to S3 storage and monitoring dashboard.
  • Team training on new functionality.
  • Post-launch support — bug fixes and optimization for one month.

Work Process

  1. Analysis. Determine file types, maximum size, storage location.
  2. Design. Decide whether chunked is needed, where to store (S3/MinIO/local).
  3. Implementation. Write controllers, validation, client code with progress bar.
  4. Testing. Upload files of various sizes, check errors, timeouts, security.
  5. Deployment. Configure S3, CI/CD, monitoring.

Timeline and Cost

File upload with validation in S3 for Laravel/Node.js: 1–2 days. Chunked upload + progress bar: 2–3 days. Cost is calculated individually — write to us, and we'll estimate your project. We work under contract with a quality guarantee — 5+ years of experience, over 30 projects with file upload.

Contact us for a consultation if you want to implement reliable file upload without surprises. Order the implementation — and we'll do it turnkey with a guarantee.