Loading file entities programmatically is a fundamental task for Drupal developers. Whether you are building a custom migration script, processing external API data, or managing media assets, you will often find yourself with a file URI (like public://images/photo.jpg) and need to retrieve the corresponding File entity object.
In older versions of Drupal, this was handled by procedural functions. However, modern Drupal (Drupal 8, 9, and 10) utilizes a robust Entity API that changes how we interact with files. In this guide, you will learn the most efficient ways to load a file entity by its URI, understand why these methods work, and explore best practices for performance and maintainability.
The Modern Approach: Using loadByProperties
In modern Drupal, files are treated as full-fledged entities. To retrieve a file based on a specific attribute—such as its URI—the most straightforward method is using the loadByProperties method provided by the Entity Storage interface.
This method is highly versatile because it allows you to query the database for entities that match an associative array of conditions. Here is the standard implementation using the \Drupal static wrapper:
$uri = 'public://path/to/file.png';
/** @var \Drupal\file\FileInterface[] $files */
$files = \Drupal::entityTypeManager()
->getStorage('file')
->loadByProperties(['uri' => $uri]);
/** @var \Drupal\file\FileInterface|null $file */
$file = reset($files) ?: NULL;
if ($file) {
// You now have the File entity object
$fid = $file->id();
}
Why This Works
The entityTypeManager service retrieves the storage handler for the file entity type. When you call loadByProperties, Drupal executes a query against the file_managed table (or the relevant entity table) to find records where the uri column matches your input.
It is important to note that loadByProperties always returns an array, even if only one match is found. Using reset() is a common PHP pattern to grab the first element of that array or return FALSE if the array is empty.
Best Practice: Using Dependency Injection
While the static \Drupal::entityTypeManager() call is convenient for quick scripts or .module files, it is not the best practice for professional module development. If you are working within a Controller, Service, or Form, you should use Dependency Injection (DI).
Dependency injection makes your code easier to test and more maintainable. Here is how you would implement this logic within a custom service:
namespace Drupal\my_module;
use Drupal\Core\Entity\EntityTypeManagerInterface;
class FileResolver {
protected $entityTypeManager;
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
public function getFileByUri($uri) {
$storage = $this->entityTypeManager->getStorage('file');
$files = $storage->loadByProperties(['uri' => $uri]);
return reset($files) ?: NULL;
}
}
By injecting the entity_type_manager service, your code remains decoupled and follows Drupal's architectural standards.
High-Performance Alternative: EntityQuery
If you only need the File ID (fid) rather than the full entity object, or if you are concerned about memory usage when dealing with thousands of potential matches, using entityQuery is a more performant alternative.
loadByProperties actually loads the full entity into memory. If you just need to check if a file exists, use this approach:
$fids = \Drupal::entityQuery('file')
->condition('uri', $uri)
->accessCheck(FALSE) // Essential for file entities in many contexts
->execute();
if (!empty($fids)) {
$fid = reset($fids);
// You can now load the file only if you really need the full object
$file = \Drupal\file\Entity\File::load($fid);
}
Note on Access Check: In Drupal 9.2+ and Drupal 10, calling accessCheck() is mandatory for entity queries. For files, you usually set this to FALSE unless you specifically want to filter by the user's permission to view the file entity.
Transitioning from Drupal 7
If you are coming from a Drupal 7 background, you might remember using file_load_multiple(). In D7, the code looked like this:
// The old Drupal 7 way
$files = file_load_multiple(array(), array('uri' => $uri));
$file = reset($files);
In modern Drupal, file_load_multiple() has been deprecated and removed. The move to the Entity API is part of Drupal's shift toward Object-Oriented Programming (OOP). While the syntax is slightly more verbose, it provides a unified way to handle all entity types (nodes, users, files, etc.) using the same logic.
Common Mistakes to Avoid
- Assuming URIs are Unique: While Drupal generally prevents duplicate URIs in the
file_managedtable, it is technically possible for multiple entities to point to the same URI in some edge cases (e.g., failed migrations or manual database manipulation). Always handle the result ofloadByPropertiesas an array. - Case Sensitivity: Database systems handle string comparisons differently. Depending on your database collation (MySQL vs. PostgreSQL), searching for
public://Image.jpgmight not returnpublic://image.jpg. Always ensure your URI strings are normalized. - File Existence vs. Entity Existence: Remember that loading a file entity only tells you that a record exists in the Drupal database. It does not guarantee that the physical file exists on the disk. Use the
file_systemservice if you need to verify the physical file's presence.
Frequently Asked Questions
What if I have the file path instead of the URI?
If you have a relative path like /sites/default/files/photo.jpg, you first need to convert it to a URI. You can use the file_url_generator service or manually prepend the stream wrapper (e.g., public://). However, Drupal's internal logic almost always prefers the scheme:// format.
Does this work for private files?
Yes. The loadByProperties method works for all stream wrappers, including public://, private://, and even external wrappers like s3://. As long as the file is registered in the file_managed table, Drupal will find it.
Is there a performance penalty for loading by URI?
The uri field in the file_managed table is indexed. This means that lookups are extremely fast, even on sites with hundreds of thousands of files. However, for maximum performance, always prefer entityQuery if you only need the ID.
Wrapping Up
Loading a file entity by URI is a core skill for any Drupal developer. By moving away from procedural functions and embracing the EntityTypeManager, you ensure your code is compatible with Drupal 10 and beyond.
Key Takeaways:
- Use loadByProperties(['uri' => $uri]) for a simple, entity-based approach.
- Use Dependency Injection in your classes for cleaner, testable code.
- Use entityQuery if performance is a priority and you only need the File ID.
- Always remember that the URI is the "address" of the file in Drupal's virtual file system, but it doesn't replace the need to check for physical file existence when necessary.