Temporary URL for local driver in Spatie Laravel media library
Tags: laravel, php
Published: 2020-12-02 11:49:35

Today’s guide describes the implementation of temporary URL for local driver in `spatie/laravel-medialibrary`. We'll accomplish this via [Laravel signed URLs](https://laravel.com/docs/8.x/urls#signed-urls).

If you use getTemporaryUrl() function of the `laravel-medialibrary` for local driver, you will get the following error:
*RuntimeException with message 'This driver does not support creating temporary URLs.'*

The below steps describe how to implement getTemporaryUrl function in your Laravel app.


## Step 1: Add a private disk to config file
Laravel already comes with local, public and s3 disks and you should add your own disk for private files to `config/filesystems`.

```
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Here you may configure as many filesystem "disks" as you wish, and you
| may even configure multiple disks of the same driver. Defaults have
| been setup for each driver as an example of the required options.
|
| Supported Drivers: "local", "ftp", "sftp", "s3", "rackspace"
|
*/

'disks' => [
    'private' => [
        'driver' => 'local',
        'root' => storage_path('app/private'),
        'url' => env('APP_URL').'/private-storage',
        'visibility' => 'private',
    ],
],
```

## Step 2: Add files to the private disk
You must ensure that files added to `secret-files` collection are automatically added to the private disk.
```
// in your model

public function registerMediaCollections(): void
{
    $this
       ->addMediaCollection('secret-files')
       ->useDisk('private');
}
```

## Step 3: Create a controller that handle file requests
It does nothing but returning the media model instance.
Note: You can even authorise using Laravel policy.
```
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Storage;

class ServePrivateStorage extends Controller
{
    /**
     * Handle the incoming request.
     *
     * @param \Spatie\MediaLibrary\MediaCollections\Models\Media $media
     * @return \Illuminate\Http\Response
     */
    public function __invoke(Media $media)
    {
        // $this->authorize('view', $media->model);

        return $media;
    }
}
```

## Step 4: Create the controller's signed route
```
Route::get('private-storage/{media}/{filename}', 'ServePrivateStorage')
    ->middleware('signed')
    ->name('private-storage');
```

## Step 5: Now we need a media URL generator class
In this step we extend the `DefaultUrlGenerator` and override the `getTemporaryUrl` function.
```
namespace App\Services;

use DateTimeInterface;
use Illuminate\Support\Facades\URL;
use Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator;

class MediaUrlGeneratorService extends DefaultUrlGenerator
{
    public function getTemporaryUrl(DateTimeInterface $expiration, array $options = []): string
    {
        return URL::temporarySignedRoute(
            'private-storage',
            $expiration,
            ['media' => $this->media, 'filename' => $this->media]
        );
    }
}
```

## Step 6: Replace your URL generator with the default one
Open the `config/media-library.php` file and replace the following class with yours.
```
'url_generator' => Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator::class,
```
<br />
That's it. Now your local driver does support creating temporary URLs.