A file upload is a two-stage problem. Drupal saves an uploaded file as temporary the moment it arrives — a housekeeping process periodically deletes temporary files nobody ever finished using. Your submit handler's job is to mark the file permanent once you're sure it's actually being kept, or that upload silently vanishes within a day or two.
What you'll learn in this lesson
- Why an uploaded file starts out temporary, not permanent
- How to load the File entity from a
managed_fileelement's submitted value - The two calls needed to keep it:
setPermanent()andsave()
Reading the uploaded file's ID
$fids = $form_state->getValue('document');
$fid = reset($fids);
A #type => 'managed_file' element submits an array of file IDs (fids), even when only one file was uploaded — it's built to support multiple files. reset() grabs the first one; for a genuinely multi-file field you'd loop over the whole array instead.
Making the file permanent
if ($fid) {
$file = \Drupal\file\Entity\File::load($fid);
if ($file) {
$file->setPermanent();
$file->save();
}
}
Load the File entity by its ID, call setPermanent() to flag it as no longer temporary, then save() to persist that flag change. Skipping this step is the single most common file-upload bug in Drupal modules — the upload appears to work, then the file is gone a day or two later when Drupal's cron cleans up temporary files.
file_save() existed in Drupal 7 but was removed entirely in Drupal 8. The modern equivalent is exactly what's shown above — load the File entity and call its own ->save() method, or use the injected file.repository service (\Drupal::service('file.repository')) for writing brand-new file data programmatically rather than handling an already-uploaded managed_file value.Quick check: a user uploads a file, but you never call
setPermanent()on it in your submit handler. What happens to the file? It stays flagged as temporary and gets deleted automatically the next time Drupal's cron runs its temporary-file cleanup — usually within 24 hours, even though the upload itself succeeded.
Key takeaways
- A
managed_fileelement's submitted value is always an array of file IDs, even for a single file. - Uploaded files start temporary — you must explicitly call
setPermanent()andsave()to keep them past the next cron run. file_save()is dead Drupal 7 code; use the File entity's ownsave()method, or thefile.repositoryservice for writing new file data programmatically.- Load the File entity with
File::load($fid)before calling any methods on it — the fid alone isn't the entity.
Coming up next
You've now covered the standard shapes a submit handler takes — redirecting, saving config, saving entities, saving files. The final lesson in this section pulls it together: triggering genuinely custom logic, like sending an email or calling an external API, from inside a submit handler.