sajad torkamani

In a nutshell

Laravel provides a powerful filesystem abstraction based on the Flysystem PHP package. The Laravel Flysystem integration provides drivers for working with:

  • Local filesystems
  • Amazon S3
  • SFTP

Laravel’s filesystem configuration is stored at config/filesystems.php. Here you can configure all your filesystem’s “disks”.

Each disk represents a particular storage driver and storage location. Here’s the default config file at the time of writing (August, 2024):

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Filesystem Disk
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default filesystem disk that should be used
    | by the framework. The "local" disk, as well as a variety of cloud
    | based disks are available to your application for file storage.
    |
    */

    'default' => env('FILESYSTEM_DISK', 'local'),

    /*
    |--------------------------------------------------------------------------
    | Filesystem Disks
    |--------------------------------------------------------------------------
    |
    | Below you may configure as many filesystem disks as necessary, and you
    | may even configure multiple disks for the same driver. Examples for
    | most supported storage drivers are configured here for reference.
    |
    | Supported drivers: "local", "ftp", "sftp", "s3"
    |
    */

    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
            'throw' => false,
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
            'throw' => false,
        ],

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
            'throw' => false,
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Symbolic Links
    |--------------------------------------------------------------------------
    |
    | Here you may configure the symbolic links that will be created when the
    | `storage:link` Artisan command is executed. The array keys should be
    | the locations of the links and the values should be their targets.
    |
    */

    'links' => [
        public_path('storage') => storage_path('app/public'),
    ],

];

Local vs S3 driver

Local driver

When using the local driver, all file operations are relative to the root directory defined in your filesystems configuration file. By default, this value is set to the storage/app directory. Therefore, the following method would write to storage/app/example.txt:

use Illuminate\Support\Facades\Storage;
 
Storage::disk('local')->put('example.txt', 'Contents');

S3 driver

Before using the S3 driver, you’ll need to install the Flysystem S3 package:

composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies

An S3 disk configuration array is located in your config/filesystems.php configuration file. Typically, you should configure your S3 information and credentials using the following environment variables which are referenced by the config/filesystems.php configuration file:

AWS_ACCESS_KEY_ID=<your-key-id>
AWS_SECRET_ACCESS_KEY=<your-secret-access-key>
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=<your-bucket-name>
AWS_USE_PATH_STYLE_ENDPOINT=false

The public disk

The public disk included in your application’s filesystems configuration file is intended for files that are going to be publicly accessible. By default, the public disk uses the local driver and stores its files in storage/app/public.

To make these files accessible from the web, you should create a symbolic link from public/storage to storage/app/public.

To create this symbolic link, you may use the storage:link Artisan command:

php artisan storage:link

Once a file has been stored and the symbolic link has been created, you can create a URL to the files using the asset helper:

echo asset('storage/file.txt');

You may configure additional symbolic links in your filesystems configuration file. Each of the configured links will be created when you run the storage:link command:

'links' => [
    public_path('storage') => storage_path('app/public'),
    public_path('images') => storage_path('app/images'),
],

The storage:unlink command may be used to destroy your configured symbolic links:

php artisan storage:unlink
How to create & remove symbolic links for file storage in Laravel

Scoped & read-only filesystems

Scoped filesystems

Scoped disks allow you to define a filesystem where all paths are automatically prefixed with a given path prefix. Before creating a scoped filesystem disk, you will need to install an additional Flysystem package via the Composer package manager:

composer require league/flysystem-path-prefixing "^3.0"

You may create a path scoped instance of any existing filesystem disk by defining a disk that utilizes the scoped driver. For example, you may create a disk which scopes your existing s3 disk to a specific path prefix, and then every file operation using your scoped disk will utilize the specified prefix:

's3-videos' => [
    'driver' => 'scoped',
    'disk' => 's3',
    'prefix' => 'path/to/videos',
],

Read-only file systems

“Read-only” disks allow you to create filesystem disks that do not allow write operations. Before using the read-only configuration option, you will need to install an additional Flysystem package via the Composer package manager:

composer require league/flysystem-read-only "^3.0"

Next, you may include the read-only configuration option in one or more of your disk’s configuration arrays:

's3-videos' => [
    'driver' => 's3',
    // ...
    'read-only' => true,
],

Obtaining a disk instance

You can use the Storage facade to interact with any of your configured disks. For example, you can use its put method to store a file on the default disk:

use Illuminate\Support\Facades\Storage;
 
Storage::put('avatars/1', $content);

If your app has multiple disks, you can use the disk method to work files on a particular disk:

Storage::disk('s3')->put('avatars/1', $content);

Use on-demand disks

Sometimes you may wish to create a disk at runtime using a given configuration without that configuration actually being present in your application’s filesystems configuration file. To accomplish this, you may pass a configuration array to the Storage facade’s build method:

use Illuminate\Support\Facades\Storage;
 
$disk = Storage::build([
    'driver' => 'local',
    'root' => '/path/to/root',
]);
 
$disk->put('image.jpg', $content);

Working with files

Read / download files

Retrieve file contents

The get method may be used to retrieve the contents of a file. The raw string contents of the file will be returned by the method. Remember, all file paths should be specified relative to the disk’s “root” location:

$contents = Storage::get('file.jpg');
Retrieve JSON file

If the file you are retrieving contains JSON, you may use the json method to retrieve the file and decode its contents:

$orders = Storage::json('orders.json');
Download a file from disk
return Storage::download('file.jpg');
 
return Storage::download('file.jpg', $filenameForDownload, $httpHeaders);

Check things about files

Check if file exists on disk
if (Storage::disk('s3')->exists('file.jpg')) {
    // ...
}
Check if file is missing on disk
if (Storage::disk('s3')->missing('file.jpg')) {
    // ...
}
Get file metadata
use Illuminate\Support\Facades\Storage;
 
$size = Storage::size('file.jpg');
$time = Storage::lastModified('file.jpg');
$mime = Storage::mimeType('file.jpg');
$path = Storage::path('file.jpg');

File URLs

Get a file URL from a local driver

If you are using the local driver, this will typically just prepend /storage to the given path and return a relative URL to the file.

use Illuminate\Support\Facades\Storage;
 
$url = Storage::url('file.jpg');

Storage::url() will typically just prepend /storage to the given path and return a relative URL to the file.

When using the local driver, all files that should be publicly accessible should be placed in the storage/app/public directory. Furthermore, you should create a symbolic link at public/storage which points to the storage/app/public directory.

When using the local driver, the return value of url is not URL encoded. It’s therefore recommended to always store your files using names that will create valid URLs.

Get a file URL from a s3 driver
use Illuminate\Support\Facades\Storage;
 
$url = Storage::url('file.jpg');

Storage::url() will return a fully qualified remote URL.

Generate temporary s3 URL

Using the temporaryUrl method, you may create temporary URLs to files stored using the s3 driver. This method accepts a path and a DateTime instance specifying when the URL should expire:

use Illuminate\Support\Facades\Storage;
 
$url = Storage::temporaryUrl(
    'file.jpg', now()->addMinutes(5)
);

If you need to specify additional S3 request parameters, you may pass the array of request parameters as the third argument to the temporaryUrl method:

$url = Storage::temporaryUrl(
    'file.jpg',
    now()->addMinutes(5),
    [
        'ResponseContentType' => 'application/octet-stream',
        'ResponseContentDisposition' => 'attachment; filename=file2.jpg',
    ]
);

Store a file

The put method may be used to store file contents on a disk. You may also pass a PHP resource to the put method, which will use Flysystem’s underlying stream support. Remember, all file paths should be specified relative to the “root” location configured for the disk:

use Illuminate\Support\Facades\Storage;
 
Storage::put('file.jpg', $contents);
 
Storage::put('file.jpg', $resource);

If the put method (or other “write” operations) is unable to write the file to disk, false will be returned:

if (! Storage::put('file.jpg', $contents)) {
    // The file could not be written to disk...
}
Upload file to default disk

You can use the store() method to upload to your default disk:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
 
class UserAvatarController extends Controller
{
    /**
     * Update the avatar for the user.
     */
    public function update(Request $request): string
    {
        $path = $request->file('avatar')->store('avatars');
 
        return $path;
    }
}

By default, the store method will generate a unique ID to serve as the filename. The file’s extension will be determined by examining the file’s MIME type. The path to the file will be returned by the store method so you can store the path, including the generated filename, in your database.

Upload file to specific disk
$path = $request->file('avatar')->store(
    'avatars/'.$request->user()->id, 's3'
);
Stream files to storage

Streaming files to storage offers significantly reduced memory usage. If you would like Laravel to automatically manage streaming a given file to your storage location, you may use the putFile or putFileAs method. This method accepts either an Illuminate\Http\File or Illuminate\Http\UploadedFile instance and will automatically stream the file to your desired location:

use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
 
// Automatically generate a unique ID for filename so you can store in DB if needed...
$path = Storage::putFile('photos', new File('/path/to/photo'));
 
// Manually specify a filename...
$path = Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg');

The putFile and putFileAs methods also accept an argument to specify the “visibility” of the stored file. This is particularly useful if you are storing the file on a cloud disk such as Amazon S3 and would like the file to be publicly accessible via generated URLs:

Storage::putFile('photos', new File('/path/to/photo'), 'public');
Prepend / append to files
Storage::prepend('file.log', 'Prepended Text');
 
Storage::append('file.log', 'Appended Text');

Delete a file

From default disk:

use Illuminate\Support\Facades\Storage;
 
Storage::delete('file.jpg');
 
Storage::delete(['file.jpg', 'file2.jpg']);

From specific disk:

use Illuminate\Support\Facades\Storage;
 
Storage::disk('s3')->delete('path/file.jpg');

Explore the filesystem

Get files in a directory
use Illuminate\Support\Facades\Storage;
 
# Get files in a directory
$files = Storage::files($directory);

# Get files in a directory and all subdirectories
$files = Storage::allFiles($directory);
Get directories within a directory
# Get files within one directory
$directories = Storage::directories($directory);
 
# Get files within a directory and all its subdirectories
$directories = Storage::allDirectories($directory);
Create directory
Storage::makeDirectory($directory);
Delete directory
Storage::deleteDirectory($directory);
Copy / move files
Storage::copy('old/file.jpg', 'new/file.jpg');
 
Storage::move('old/file.jpg', 'new/file.jpg');

File visibility

In Laravel’s Flysystem integration, “visibility” is an abstraction of file permissions across multiple platforms. Files may either be declared public or private. When a file is declared public, you are indicating that the file should generally be accessible to others. For example, when using the S3 driver, you may retrieve URLs for public files.

You can set the visibility when writing the file via the put method:

use Illuminate\Support\Facades\Storage;
 
Storage::put('file.jpg', $contents, 'public');

If the file has already been stored, its visibility can be retrieved and set via the getVisibility and setVisibility methods:

$visibility = Storage::getVisibility('file.jpg');
 
Storage::setVisibility('file.jpg', 'public');

When interacting with uploaded files, you may use the storePublicly and storePubliclyAs methods to store the uploaded file with public visibility:

$path = $request->file('avatar')->storePublicly('avatars', 's3');
 
$path = $request->file('avatar')->storePubliclyAs(
    'avatars',
    $request->user()->id,
    's3'
);

Local Files and Visibility

When using the local driver, public visibility translates to 0755 permissions for directories and 0644 permissions for files. You can modify the permissions mappings in your application’s filesystems configuration file:

'local' => [
    'driver' => 'local',
    'root' => storage_path('app'),
    'permissions' => [
        'file' => [
            'public' => 0644,
            'private' => 0600,
        ],
        'dir' => [
            'public' => 0755,
            'private' => 0700,
        ],
    ],
    'throw' => false,
],

Testing files

See here.

Sources & further reading

Tagged: Laravel