File API

Drupal's services for saving, copying, moving and deleting files by stream wrapper URI, plus file entities and usage records that track each managed file.

Learn DrupalDrupal glossary

The File API is the group of core services that handle files on a Drupal site. Code addresses files by stream wrapper URI, such as public://reports/export.csv, instead of a server path. Two layers exist: file_system works on the disk only, and file.repository also keeps a file entity in the database.

How the File API works in Drupal 11

The file_system service (FileSystemInterface) copies, moves, deletes and writes files and checks directories with prepareDirectory(). It makes no database change and invokes no hooks. Its API documentation says to use delete() for a file that has no row in the files table.

The file.repository service (FileRepositoryInterface) does the same jobs for files that have a record: writeData(), copy(), move() and loadByUri(). It creates or updates the file entity. A file written by writeData() is marked permanent.

The file.usage service (FileUsageInterface) records which module uses a file for which object, for example a node. listUsage() returns those references, and the cron cleanup of temporary files skips any file that still has one.

The URI scheme decides where a file lives. public:// is for files served directly by the web server. private:// is for files served by Drupal; core registers that wrapper only when $settings['file_private_path'] is set. temporary:// is described in core as temporary local files for uploads and previews.

When a destination already exists, the methods take a FileExists enum with the cases Rename, Replace and Error. The integer constants on FileSystemInterface were deprecated in Drupal 10.3.0 and are removed from Drupal 12.0.0.

Example

This code stores a generated CSV string so that it shows up as a file entity:

use Drupal\Core\File\FileExists;
        use Drupal\Core\File\FileSystemInterface;

        $directory = 'public://reports';
        \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);
        $file = \Drupal::service('file.repository')->writeData($csv, $directory . '/export.csv', FileExists::Replace);

Common mistakes

  • Using file_system for a file that has an entity. Deleting or moving it there leaves a file record pointing at nothing. Move it with file.repository and delete the file entity itself.
  • Hard-coding a server path. A URI such as public://reports still resolves when the files directory is configured elsewhere.
  • Writing a custom upload without validation. The core upload topic says to always validate against a list of allowed extensions. A managed_file element with such a list also blocks insecure extensions by default.
  • Turning on allow_insecure_uploads. Potentially insecure files are then no longer renamed, and core documents the setting as not recommended.

For marketing and content teams

Files that must not be public, such as paid downloads, need the private file system, which a developer configures in settings.php. The File system settings page at /admin/config/media/file-system has a "Delete temporary files after" option. Its description warns that deleted temporary files may not be recoverable. Related ideas: entities, cron and image styles.

Sources

Questions about the File API

The file on disk is just bytes at a path. The file entity is the database record for it, with the URI, owner and status. Usage is recorded separately by the file.usage service.

A file that was uploaded but never made permanent is temporary. Cron deletes temporary files that nothing uses once they are older than system.file:temporary_maximum_age. Saving content that references the file records a usage, and recording a usage makes the file permanent.

No. Drupal 11.4 does not define it. To save a string as a managed file, call writeData() on the file.repository service. For a plain file without an entity, use saveData() on file_system.

Related terms

All Drupal terms from A to Z