Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Azure File Storage Driver Laravel Package

academe/laravel-azure-file-storage-driver

A Laravel filesystem driver that connects Azure File Storage to Laravel's Storage facade. Add an Azure disk to your config and read, write, and delete files through the same familiar API you use for local and S3 storage.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with Composer: composer require academe/laravel-azure-file-storage-driver. Before you commit, run composer why-not academe/laravel-azure-file-storage-driver if you're on Laravel 10/11 — the last tagged release is from mid‑2022, so Illuminate/Flysystem constraints may need review (see Gotchas).
  • The package is auto-discovered: its service provider calls Storage::extend(...) (or the equivalent Flysystem adapter registration) to attach a driver name — typically azure-file — to Laravel's FilesystemManager.
  • Add a disk in config/filesystems.php:
'azure-files' => [
    'driver'   => 'azure-file',                       // confirm exact string in the package's provider/README
    'name'     => env('AZURE_FILE_ACCOUNT'),
    'key'      => env('AZURE_FILE_KEY'),
    'container'=> env('AZURE_FILE_SHARE'),            // this is an Azure File *share*, not a blob container
    'endpoint' => env('AZURE_FILE_ENDPOINT', 'https://'.env('AZURE_FILE_ACCOUNT').'.file.core.windows.net'),
    'url'      => env('AZURE_FILE_URL'),              // optional; enables Storage::url()
],
  • Put credentials in .env only. Prefer a storage-account key or a SAS token scoped to the share. Create the share first (az storage share create --name my-share --account-name ... or via Portal/Bicep) — drivers like this manage files and directories inside a share; they do not provision the share itself.
  • First use case to validate the wiring: from php artisan tinker,
Storage::disk('azure-files')->put('invoices/2024/001.pdf', $pdf);
Storage::disk('azure-files')->exists('invoices/2024/001.pdf'); // true
Storage::disk('azure-files')->files('invoices/2024');
  • Where to look first: (1) the README's config key table (the exact key names have moved between versions), (2) the package's service provider for the registered driver string, and (3) the underlying Flysystem adapter it wraps (academe/flysystem-azure-file-storage) for the real semantics of temporaryUrl, visibility, and directory listing. The Laravel layer is thin; the adapter is where behavior lives.

Implementation Patterns

  • Use the normal Laravel filesystem API. Once the disk is registered, everything goes through Storage::disk('azure-files'): put, putFile, putFileAs, get, getStream, exists, size, lastModified, copy, move, delete, makeDirectory, deleteDirectory, files, allFiles, directories. No Azure-specific code should leak into business logic — that's the point of the driver.
  • Upload flow. Keep the relative path in the database, not the full URL:
$path = $request->file('invoice')->store('invoices/'.$tenantId, 'azure-files');
Invoice::create(['path' => $path]);
  • Stream large files. put()/get() load the whole payload into memory. For anything above a few MB, use writeStream / readStream / Storage::disk(...)->getDriver()->readStream($path) and return it from a controller with response()->stream(...). This also lets Azure handle the transfer and avoids PHP memory limits.
  • Serve files through a controller when shares aren't public. Azure File shares have no public-access mode. If temporaryUrl() is supported by your adapter version, use it with a short expiry (Storage::disk('azure-files')->temporaryUrl($path, now()->addMinutes(10))) — it returns a SAS-signed URL. If not, stream through a controller after your own auth check, or generate a SAS manually with the Azure SDK.
  • Isolate tenants/apps with root. If several apps share one share, set 'root' => 'app-name' on each disk so paths can't collide, and keep path building ("invoices/{$id}.pdf") relative to that root.
  • Programmatic disks. For per-tenant shares, build disks on the fly with Storage::build([...]) (Laravel 9+) or config()->set('filesystems.disks.tenant', [...]) from a service provider or middleware. This is also how you switch driver by environment: local in dev, Azure in staging/prod, behind one Storage::disk(config('filesystems.default')) call.
  • Queue-friendly. Pass paths, not contents, into jobs. Jobs on workers need the same .env credentials. Add a retry policy for 5xx/ServiceException responses ($this->release(30)) so transient Azure hiccups don't fail the job.
  • Copy/move is server-side within a share. Storage::disk(...)->move('a/x.pdf', 'b/x.pdf') is a metadata/copy operation against Azure; use it freely inside one disk. Across shares/accounts it won't work — download + upload.
  • Cleanup job. Schedule a command that walks allFiles(...) and prunes on lastModified() older than N days. Beware: listing is per-directory and can be slow on large shares; keep a prefix scheme (tmp/) to limit scope.
  • Testing. Storage::fake('azure-files') swaps in a local temp disk and keeps all assertions (assertExists, assertMissing) working — use it for feature tests. Keep one opt-in integration test against a real or emulated share (gated on env vars) to catch driver-specific issues like path encoding and temporaryUrl.

Gotchas and Tips

  • File Storage ≠ Blob Storage. This driver targets Azure Files (SMB shares). Blob-specific features — public containers, CDN endpoints, visibility('public'), direct url() — do not apply. If you actually want Blob, use league/flysystem-azure-blob-storage instead. This is the single most common misconfiguration.
  • Visibility is a no-op. ACLs are share-level, not file-level. Expect visibility()/setVisibility() to either do nothing or throw UnableToSetVisibility; don't build authorization on top of them.
  • Naming rules are strict and enforced by Azure, not Laravel. Share names: lowercase, 3–63 chars, alphanumeric + hyphens. File/directory paths: max ~2048 characters total, and the characters " \ / : | < > * ? plus control chars are invalid (also be careful with % and # in URLs). Sanitize user-supplied filenames before they reach the disk.
  • Case collisions. Azure Files is case-insensitive but case-preserving. Logo.png and logo.png are the same file — two uploads will silently overwrite. Normalize paths in your app if uniqueness matters.
  • Timestamp/return-type differences. lastModified() returns a Unix timestamp or a Carbon depending on the Flysystem version. Normalize in one place rather than sprinkling Carbon::createFromTimestamp everywhere.
  • Locking / 409 Conflicts. SMB leases (Office files, some sync clients) and concurrent writers cause 409 Conflict or 423 Locked. Wrap writes/deletes in a small retry with backoff; log $e->getErrorCode() from the underlying ServiceException. A 409 can also mean the parent directory doesn't exist — call makeDirectory or put with a path whose ancestors already exist.
  • Error types. Failures surface as Flysystem exceptions (UnableToWriteFile, UnableToReadFile) wrapping MicrosoftAzure\Storage\Common\Exceptions\ServiceException. Catch the Flysystem type in app code, and dig into getErrorCode() only in logs.
  • Listing limits and cost. allFiles() recurses — one request per directory. On deep trees this is slow and bills per transaction. Prefer files($prefix) with a sharded prefix scheme, and cache listings for read-heavy screens (Laravel 9+'s 'cache' disk option may not be honored by this older adapter — verify).
  • url must be configured. If url is missing in the disk config, Storage::url() returns a bare path, not a usable link. For anything user-facing, rely on temporaryUrl() or a controller route and test that the SAS actually works from a browser.
  • Package maintenance risk. Stars are low, last release 2022, and it's one maintainer. Pin the version in composer.json, and budget time for a fork if microsoft/azure-storage-file (or the maintainer's fork) trips PHP 8.1+ deprecations or an Illuminate constraint. Run a smoke test in the target Laravel version before architecture decisions depend on it.
  • Azurite isn't a great File emulator. Blob support is mature; File share support is patchier. For integration tests, a cheap dev storage account is usually less painful than fighting the emulator.
  • Debugging. There's no built-in logging. Start with php artisan tinker and a single put/get. If that fails, check (in order): driver name typo, share exists, credentials/permissions (403 vs 404 tells you which), endpoint for sovereign clouds (*.file.core.chinacloudapi.cn, etc.), then path characters. If you need request-level logs, you'll have to wire a Guzzle handler into the SDK client yourself — the adapter usually doesn't expose middleware hooks.
  • Config caching. php artisan config:cache after every filesystems.php or .env change; env() called outside config files returns null in cached deployments and is a common "works on my machine" failure.
  • Extension points. Because the driver is registered through Storage::extend, you can re-register your own driver string in a later provider, or subclass the adapter and override temporaryUrl/path mapping. That's the cleanest escape hatch if the package's URL or visibility behavior doesn't match your needs.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package