Letting visitors upload files is one thing; making sure they can't upload a 500MB video to your "profile photo" field, or a disguised executable renamed to .jpg, is another. File uploads get their own dedicated validation mechanism in Drupal — #upload_validators — rather than reusing the generic #element_validate pattern.
What you'll learn in this lesson
- How
#upload_validatorsattaches file-specific validation constraints declaratively - The built-in validators for file extension and file size
- Why file validation is checked differently from a plain text field's validation
- How to combine multiple upload validators on the same field
Declaring upload validators
$form['photo'] = [
'#type' => 'managed_file',
'#title' => $this->t('Profile photo'),
'#upload_location' => 'public://profile-photos',
'#upload_validators' => [
'FileExtension' => ['extensions' => 'jpg jpeg png'],
'FileSizeLimit' => ['fileLimit' => 2 * 1024 * 1024],
],
];
#upload_validators is an associative array where each key names a validation plugin and each value is the array of arguments that plugin expects. FileExtension restricts uploads to a space-separated list of allowed extensions; FileSizeLimit caps the file size in bytes (here, 2MB, expressed as 2 * 1024 * 1024 for readability rather than the raw number). Both run automatically the moment a file is uploaded — before the rest of the form is even submitted.
Why this differs from a normal field's validation
A managed_file or file element uploads and validates its file over its own AJAX request, separate from the form's main submission — that's how you get instant feedback the moment a bad file is selected, rather than waiting for the whole form to be submitted first. #upload_validators plugs into that upload-time check specifically, which is why file validation uses this dedicated property instead of #element_validate or validateForm().
Stacking multiple constraints
'#upload_validators' => [
'FileExtension' => ['extensions' => 'pdf doc docx'],
'FileSizeLimit' => ['fileLimit' => 10 * 1024 * 1024],
'FileImageDimensions' => ['maxDimensions' => '0x0', 'minDimensions' => '100x100'],
],
Every entry in the array is checked; a file must pass all of them to be accepted. Drupal core ships several built-in validator plugins beyond extension and size — FileImageDimensions for minimum/maximum pixel dimensions, for instance — and contrib modules can add their own for cases core doesn't cover.
FileExtension checks the filename's extension, not the file's actual binary content — a malicious file renamed to end in .jpg would pass this specific check. Drupal's file system does additional MIME-type sniffing separately, but if you're accepting uploads from untrusted users, treat extension validation as one layer of defense, not the whole story.See it for yourself
Add a managed_file field with FileExtension restricted to jpg jpeg png and a 2MB FileSizeLimit. Try uploading a .txt file and confirm it's rejected immediately, before you even submit the rest of the form.
Quick check: you set
'FileSizeLimit' => ['fileLimit' => 2 * 1024 * 1024]. Why write the number as an expression instead of just the literal2097152? (Answer: pure readability — PHP evaluates the expression at parse time either way, so there's no performance difference, but2 * 1024 * 1024immediately communicates "2 megabytes" to anyone reading the code, while the raw byte count doesn't.)
Key takeaways
#upload_validatorsis a dedicated property for file-specific validation, separate from#element_validate/validateForm().- Each key in the array names a validator plugin (
FileExtension,FileSizeLimit,FileImageDimensions, and more); each value is that plugin's argument array. - File validation runs at upload time via the element's own AJAX request, giving instant feedback before the rest of the form is submitted.
- Extension checking alone isn't a full security guarantee — it checks the filename, not the file's actual binary contents.
Coming up next
You've now seen every mechanism for detecting invalid input — element-level, form-level, required-field, and file-specific. Next: how those detected errors actually get displayed to the person filling out the form, and what control you have over that presentation.