Reader Stacks

How to Upload Multiple Files in Angular

The multiple attribute on a file input returns a FileList, not a single File — looping through it to build the FormData is the one real difference from a single-file upload.

Uploading multiple files at once builds directly on the single-file upload pattern covered elsewhere on this site — the multiple attribute on the file input, and looping through the resulting FileList to build a multi-file FormData payload, are the real differences.

The template with a multi-file input

Capturing multiple selected files

selectedFiles: File[] = [];

onFilesSelected(event: Event): void {
    const input = event.target as HTMLInputElement;
    if (input.files) {
        this.selectedFiles = Array.from(input.files);
    }
}

input.files is a FileList, not a genuine JavaScript array — Array.from() converts it into a real array, which is what then allows using standard array methods (.map(), .forEach(), .filter()) on the selected files.

Displaying the selected files before upload

  • {{ file.name }} ({{ (file.size / 1024).toFixed(1) }} KB)
removeFile(fileToRemove: File): void {
    this.selectedFiles = this.selectedFiles.filter(file => file !== fileToRemove);
}

Building FormData with multiple files

onSubmit(): void {
    const formData = new FormData();

    this.selectedFiles.forEach((file, index) => {
        formData.append(`files[${index}]`, file, file.name);
    });

    this.http.post('/api/uploads', formData).subscribe({
        next: () => console.log('Upload successful'),
        error: (err) => console.error('Upload failed', err),
    });
}

Appending each file with an indexed key (files[0], files[1], and so on) matches the array-style field naming a Laravel backend expects for multi-file validation and processing — following the array validation pattern covered elsewhere on this site for handling this on the server side.

The Laravel side, handling multiple uploaded files

public function store(Request $request)
{
    $request->validate([
        'files' => 'required|array',
        'files.*' => 'file|mimes:jpeg,png,pdf|max:5120',
    ]);

    $paths = [];
    foreach ($request->file('files') as $file) {
        $paths[] = $file->store('uploads', 'public');
    }

    return response()->json(['paths' => $paths]);
}

Validating total combined file size across all selected files

onFilesSelected(event: Event): void {
    const input = event.target as HTMLInputElement;
    if (input.files) {
        const files = Array.from(input.files);
        const totalSize = files.reduce((sum, file) => sum + file.size, 0);

        if (totalSize > 20 * 1024 * 1024) {
            this.uploadError = 'Total file size must be under 20MB.';
            return;
        }

        this.selectedFiles = files;
        this.uploadError = null;
    }
}

Beyond validating each individual file's size, checking the combined total across every selected file catches a scenario where several individually-acceptable files together exceed a reasonable overall upload limit — worth adding for any multi-file upload where cumulative size, not just per-file size, genuinely matters.

Tracking upload progress for a multi-file upload

Following the same reportProgress/observe: 'events' pattern covered for single-file uploads elsewhere on this site, progress tracking works identically for a multi-file FormData payload — the reported progress simply reflects the combined upload of the entire request body, all files included together.