Creating an Azure storage adapter for Laravel

Published by at 23rd October 2016 11:25 pm

UPDATE: This post has now been superseded by this one as I've released this integration as a package.

About a year ago I was working on my first non-trivial Laravel application. The client had, for their own reasons, wanted to use Microsoft's Azure platform, particularly for its blob storage functionality, which is somewhat comparable to Amazon S3. Now, Laravel has the excellent Storage facade that allows consistent access to both local files and those stored on various file hosting services, which is built on top of Flysystem. Flysystem has an Azure driver, but the Laravel storage doesn't include support for it, so at the time I resigned myself to using Flysystem directly, which wasn't actually that bad, but not ideal.

A few days ago I stumbled across this section of the Laravel documentation, which had me kicking myself. It's actually trivially easy to implement a custom filesystem for Laravel if it already has a Flysystem adapter, as demonstrated in their Dropbox implementation in the docs. Using this as a guide, I was able to produce the following service provider for using Azure as a storage backend very quickly:

1<?php
2
3namespace App\Providers;
4
5use Storage;
6use League\Flysystem\Filesystem;
7use Illuminate\Support\ServiceProvider;
8use League\Flysystem\Azure\AzureAdapter;
9use WindowsAzure\Common\ServicesBuilder;
10
11class AzureStorageServiceProvider extends ServiceProvider
12{
13 /**
14 * Perform post-registration booting of services.
15 *
16 * @return void
17 */
18 public function boot()
19 {
20 Storage::extend('azure', function($app, $config) {
21 $endpoint = sprintf(
22 'DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s',
23 $config['name'],
24 $config['key']
25 );
26
27 $blobRestProxy = ServicesBuilder::getInstance()->createBlobService($endpoint);
28 return new Filesystem(new AzureAdapter($blobRestProxy, $config['container']));
29 });
30 }
31
32 /**
33 * Register bindings in the container.
34 *
35 * @return void
36 */
37 public function register()
38 {
39 //
40 }
41}

This should be saved as app/Providers/AzureStorageServiceProvider.php. You also need to add this to the list of service providers in config/app.php:

App\Providers\AzureStorageServiceProvider::class,

And add this to config/filesystems.php:

1 'azure' => [
2 'driver' => 'azure',
3 'name' => env('STORAGE_NAME'),
4 'key' => env('STORAGE_KEY'),
5 'container' => env('STORAGE_CONTAINER'),
6 ],

I like to also set my storage backend using environment variables in this file, as in this example:

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

That way we can easily set a different backend for testing, development and production so we don't upload files when running PHPUnit. You can also keep your other config settings in your .env file, which is always a better idea than keeping it under version control. You also need to install the microsoft/windowsazure and league/flysystem-azure packages via Composer for this to work.

As I've since changed jobs it's unlikely I'll ever actually use this Azure integration in production - it's not a service I'd choose of my own accord to use. However, since it's so straightforward to implement an adapter like this I imagine I may be doing something similar - I'm currently working on a web app that uses MongoDB for some of its data and currently stores files locally, so it might make sense to create a GridFS integration along similar lines. It may also be useful for someone else, so feel free to use it if you wish.