Three genuinely common local filesystem operations in Laravel — copying, moving, and recursively deleting a folder and its contents — are all handled through the File facade's own dedicated directory methods, distinct from the single-file methods covered elsewhere on this site.
Copying a single file
use Illuminate\Support\Facades\File;
File::copy(storage_path('app/original.pdf'), storage_path('app/backups/original-copy.pdf'));
Copying an entire directory recursively
File::copyDirectory(storage_path('app/source-folder'), storage_path('app/backup-folder'));
copyDirectory() copies the source folder's entire contents, including nested subfolders, into the destination — the destination folder is created automatically if it doesn't already exist.
Moving a file or folder
File::move(storage_path('app/temp/upload.pdf'), storage_path('app/final/upload.pdf'));
File::moveDirectory(storage_path('app/temp-folder'), storage_path('app/final-folder'));
Unlike copy/copyDirectory, move/moveDirectory remove the original — there's no separate source file or folder left behind afterward, which matters for a workflow like moving an upload from a temporary staging location into its permanent home.
Deleting a single file
File::delete(storage_path('app/temp/upload.pdf'));
// Deleting multiple files at once
File::delete([
storage_path('app/temp/file1.pdf'),
storage_path('app/temp/file2.pdf'),
]);
Deleting a folder and everything inside it, recursively
File::deleteDirectory(storage_path('app/temp-uploads'));
This is a genuinely destructive, one-shot operation with no built-in confirmation step or trash/undo mechanism — the folder and every file and subfolder inside it are removed immediately and permanently; double-checking the path variable's actual value before calling this in any automated or scripted context is worth the extra caution, since a wrong path here fails silently rather than throwing an obvious error.
Checking if a directory exists before an operation
if (File::isDirectory(storage_path('app/temp-uploads'))) {
File::deleteDirectory(storage_path('app/temp-uploads'));
}
Creating a nested directory structure that doesn't exist yet
if (! File::exists(storage_path('app/exports/2026/09'))) {
File::makeDirectory(storage_path('app/exports/2026/09'), 0755, true);
}
The third argument to makeDirectory(), set to true, enables recursive creation — without it, attempting to create a deeply nested path whose parent directories don't yet exist fails, since the method by default only creates the single final directory level, not the full chain of parents leading to it.
A practical use case: cleaning up temporary upload folders on a schedule
Schedule::call(function () {
$tempFolders = File::directories(storage_path('app/temp-uploads'));
foreach ($tempFolders as $folder) {
if (File::lastModified($folder) < now()->subHours(24)->timestamp) {
File::deleteDirectory($folder);
}
}
})->hourly();
This is a genuinely useful pattern for a feature that stages files temporarily before final processing — automatically cleaning up anything older than a set threshold prevents an ever-growing accumulation of abandoned temporary folders from consuming disk space indefinitely.