Vai al contenuto principale

Cos’è il file storage

Laravel fornisce un potente livello di astrazione basato sul pacchetto PHP Flysystem. Disco locale, SFTP, Amazon S3 e altri backend si usano con la stessa API: puoi cambiare ambiente senza modificare il codice.
Cambiando driver, il codice resta lo stesso. In sviluppo storage locale, in produzione S3: passaggio semplice.

Configurazione

La configurazione è in config/filesystems.php, dove definisci i “disk”. Un disk è la combinazione di un driver e una destinazione. Driver principali:
DriverUso
localFilesystem locale del server
publicDisco locale pubblico via web
s3Amazon S3 (o compatibile)
sftpServer SFTP
ftpServer FTP

Driver local

Usa root di filesystems. Di default storage/app/private.
use Illuminate\Support\Facades\Storage;

Storage::disk('local')->put('example.txt', 'Contents');
// Scrive in storage/app/private/example.txt

Cambiare il disk predefinito

Con FILESYSTEM_DISK.
FILESYSTEM_DISK=s3
Il disk public è per file accessibili dal web. Di default salva in storage/app/public. Per renderli raggiungibili crea il symlink public/storagestorage/app/public.
1

Crea il symlink

php artisan storage:link
2

Ottieni l'URL del file

echo asset('storage/file.txt');
Per symlink aggiuntivi configura links in config/filesystems.php.
'links' => [
    public_path('storage') => storage_path('app/public'),
    public_path('images') => storage_path('app/images'),
],
Rimuovi il symlink con:
php artisan storage:unlink

Operazioni base con la facade Storage

Lettura

use Illuminate\Support\Facades\Storage;

$contents = Storage::get('file.jpg');
$data = Storage::json('orders.json');

if (Storage::exists('file.jpg')) {
    // Esiste
}

if (Storage::missing('file.jpg')) {
    // Non esiste
}

Scrittura

Storage::put('file.jpg', $contents);
Storage::put('file.jpg', $resource);

Storage::prepend('file.log', 'Prepended Text');
Storage::append('file.log', 'Appended Text');

Storage::copy('old/file.jpg', 'new/file.jpg');
Storage::move('old/file.jpg', 'new/file.jpg');
Se put fallisce restituisce false di default. Configura 'throw' => true per lanciare un’eccezione.

Eliminazione

Storage::delete('file.jpg');
Storage::delete(['file.jpg', 'file2.jpg']);
Storage::disk('s3')->delete('path/file.jpg');

Download

return Storage::download('file.jpg');
return Storage::download('file.jpg', 'my-file.jpg', $headers);

URL

URL normale

use Illuminate\Support\Facades\Storage;

$url = Storage::url('file.jpg');
Con local è relativo (/storage/file.jpg), con s3 è l’URL completo.

URL temporaneo

Con temporaryUrl (disponibile per local e s3).
$url = Storage::temporaryUrl(
    'file.jpg',
    now()->plus(minutes: 5)
);
Su S3 puoi passare parametri aggiuntivi:
$url = Storage::temporaryUrl(
    'file.jpg',
    now()->plus(minutes: 5),
    [
        'ResponseContentType' => 'application/octet-stream',
        'ResponseContentDisposition' => 'attachment; filename=file.jpg',
    ]
);
Per URL di upload temporaneo usa temporaryUploadUrl. Utile per upload client-side diretto verso S3.
['url' => $url, 'headers' => $headers] = Storage::temporaryUploadUrl(
    'file.jpg', now()->plus(minutes: 5)
);

Upload di file

store (nome file auto)

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserAvatarController extends Controller
{
    public function update(Request $request): string
    {
        $path = $request->file('avatar')->store('avatars');
        return $path;
    }
}

storeAs (nome specifico)

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

Con disk specifico

$path = $request->file('avatar')->store(
    'avatars/' . $request->user()->id,
    's3'
);

Via facade Storage

use Illuminate\Support\Facades\Storage;

$path = Storage::putFile('avatars', $request->file('avatar'));

$path = Storage::putFileAs(
    'avatars',
    $request->file('avatar'),
    $request->user()->id
);
getClientOriginalName() e getClientOriginalExtension() sono manipolabili dall’utente: non sicuri. Usa hashName() ed extension().
$file = $request->file('avatar');

$name = $file->hashName();
$extension = $file->extension();

Visibilità

In Flysystem la visibilità pubblica/privata si gestisce con visibility.
use Illuminate\Support\Facades\Storage;

Storage::put('file.jpg', $contents, 'public');

$visibility = Storage::getVisibility('file.jpg');
Storage::setVisibility('file.jpg', 'public');
Per salvare pubblicamente file caricati usa storePublicly.
$path = $request->file('avatar')->storePublicly('avatars', 's3');

Più disk

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

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

Storage cloud (S3)

Installazione

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

Variabili d’ambiente

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
Anche servizi S3-compatibili (DigitalOcean Spaces, Cloudflare R2, Vultr Object Storage) usano il driver s3. Imposta endpoint con l’URL del servizio.
'endpoint' => env('AWS_ENDPOINT', 'https://your-endpoint.example.com'),

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');

Directory

use Illuminate\Support\Facades\Storage;

$files = Storage::files($directory);
$files = Storage::allFiles($directory);
$directories = Storage::directories($directory);

Storage::makeDirectory($directory);
Storage::deleteDirectory($directory);

Test

Storage::fake() per test senza toccare disk reali.
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

test('è possibile caricare l\'avatar', function () {
    Storage::fake('avatars');

    $file = UploadedFile::fake()->image('avatar.jpg');

    $this->post('/user/avatar', ['avatar' => $file]);

    Storage::disk('avatars')->assertExists('avatar.jpg');
    Storage::disk('avatars')->assertMissing('other.jpg');
});
UploadedFile::fake()->image() richiede l’estensione GD di PHP.

Esempio pratico: upload dell’avatar

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Storage;

class ProfileController extends Controller
{
    public function updateAvatar(Request $request): RedirectResponse
    {
        $request->validate([
            'avatar' => ['required', 'image', 'max:2048'],
        ]);

        $user = $request->user();

        if ($user->avatar_path) {
            Storage::disk('public')->delete($user->avatar_path);
        }

        $path = $request->file('avatar')->store('avatars', 'public');

        $user->update(['avatar_path' => $path]);

        return back()->with('status', 'avatar-updated');
    }
}
Nel template:
<img src="{{ Storage::disk('public')->url($user->avatar_path) }}" alt="Avatar">
Ultima modifica il 13 luglio 2026