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 Flysystem Oss Laravel Package

zing/laravel-flysystem-oss

A Laravel Flysystem adapter for Alibaba Cloud OSS that lets you use Laravel's Storage API with OSS buckets for uploading, reading, and managing files through familiar filesystem methods.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Flysystem v3 driver that plugs Alibaba Cloud OSS into Laravel's Storage facade, so day-to-day you never touch the OSS SDK directly — you add one disk and keep using Storage::disk('oss').

Install and wire up (5 minutes):

composer require zingimmick/laravel-flysystem-oss

Package discovery registers the service provider; no config/app.php edit needed. Then add a disk in config/filesystems.php and mirror the keys into .env:

'oss' => [
    'driver'            => 'oss',
    'access_key_id'     => env('OSS_ACCESS_KEY_ID'),
    'access_key_secret' => env('OSS_ACCESS_KEY_SECRET'),
    'bucket'            => env('OSS_BUCKET'),
    'endpoint'          => env('OSS_ENDPOINT'),   // e.g. oss-cn-hangzhou.aliyuncs.com
    'domain'            => env('OSS_DOMAIN'),     // optional CDN / custom domain
    'scheme'            => env('OSS_SCHEME', 'https'),
    'is_cname'          => env('OSS_IS_CNAME', false),
    'options'           => [],
],

Copy the key names from the package README rather than from a generic OSS tutorial — this fork does not use the same key names as the iidestiny package that most blog posts describe.

First use case: upload an avatar and hand back a URL.

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

Where to look first: config/filesystems.php (the disk block), then Storage::disk('oss') in tinker to confirm credentials and endpoint before writing any feature code. Storage::disk('oss')->files() returning a list is your smoke test.

Version context for 2.8.1: this release is almost entirely compatibility plumbing — Laravel 13, PHP 8.5, PHPUnit 12 — plus one behavioural fix to how the scheme config key is read. It is a safe minor bump; there are no breaking changes, but if you were relying on scheme being ignored (i.e. URLs coming out as http:// regardless of config), expect generated URLs to change.

Implementation Patterns

Make OSS the default disk, not a special case. Set FILESYSTEM_DISK=oss so Storage::put(), Storage::url(), and $request->file()->store() all route to OSS. This keeps tests and local dev swappable via a single env var.

Prefer temporaryUrl() for anything user-facing on private buckets.

return Storage::disk('oss')->temporaryUrl($path, now()->addMinutes(15));

Call it at render time (or cache the URL for slightly less than its TTL) rather than persisting signed URLs in the database — they expire and will silently 403.

Store the path, resolve the URL. Keep a relative key (avatars/2024/abc.jpg) in your model and generate the URL in an accessor. Swapping domain later (CDN migration, bucket rename) then costs you a config change instead of a data migration.

Pass per-object options on write for content type and cache headers:

Storage::disk('oss')->put($path, $contents, [
    'ContentType'        => 'image/webp',
    'CacheControl'       => 'public, max-age=31536000',
    'ContentDisposition' => 'attachment; filename="report.pdf"',
]);

The options array on the disk config applies the same defaults to every write on that disk — useful for a dedicated oss-assets disk.

Use a separate disk per bucket or per visibility concern (oss-private for uploads, oss-public/oss-cdn for assets). It is cheaper and clearer than branching on visibility inside callers, and it lets you point domain at a CDN only for the public one.

Stream large files instead of loading them. putFile()/putFileAs() and writeStream() avoid buffering whole objects in PHP memory; reserve put() with a string body for genuinely small payloads (thumbnails, JSON, generated PDFs under a few MB).

Directory semantics are prefix semantics. makeDirectory() is a no-op-ish abstraction and deleteDirectory() deletes by prefix. That is usually what you want, but be careful with deleteDirectory('uploads') when uploads-backup/ also lives under the same bucket — the prefix match is literal, so name your top-level prefixes so they cannot collide as string prefixes.

In tests, fake the disk rather than hitting OSS:

Storage::fake('oss');

and assert with Storage::disk('oss')->assertExists($path). For integration tests, run them against a dedicated throwaway bucket guarded by an env check, and clean up with deleteDirectory() in tearDown.

Laravel 13 / PHP 8.5 note: if you are on the freshly supported versions as of 2.8.1, pin with "zingimmick/laravel-flysystem-oss": "^2.8.1" — earlier 2.8.x versions declare narrower framework/PHP constraints and Composer will resolve you back to an older Laravel. The PHPUnit 12 support matters for your own suite: if you assert on Storage fakes, upgrade your test deps in the same PR so you do not straddle the Flysystem/PHPUnit matrix.

Gotchas and Tips

The scheme fix will change URLs. 2.8.1 corrects how the scheme config key is handled. If you previously set 'scheme' => 'http' or omitted it and ended up with the "wrong" scheme anyway, that behaviour is now consistent — check any code or fixtures that assert on absolute URLs, and re-check signed URLs behind a proxy where the scheme matters for signature validity.

Config key names are not portable. access_key_id/access_key_secret (this package) vs access_id/access_key (the iidestiny fork). Copying a snippet from a blog post yields a driver that boots, lists nothing, and throws an auth error that does not mention the missing key. Read the README for the version you installed.

url() returns a syntactic URL, not a permission grant. On a private bucket, Storage::url($path) looks correct and 403s in the browser. That confuses people during debugging — reach for temporaryUrl() to prove credentials work.

is_cname must match reality. If your domain is a custom domain bound to the bucket via CNAME, set is_cname => true; otherwise the adapter builds a bucket.endpoint style host and requests fail or land on the wrong host. Likewise endpoint should be the region endpoint, not a full URL with scheme — the scheme key supplies that.

Signed URLs expire, and clock skew breaks them. A container with drifting time produces UrlExpired or SignatureDoesNotMatch errors that look like credential problems. Check NTP before rotating keys.

Debug by escalating from the facade. First Storage::disk('oss')->files('/') (credentials + endpoint + bucket), then put() a tiny string (write permission), then temporaryUrl() (signing). That isolates config errors from permission errors from signature errors faster than reading stack traces.

Storage::fake('oss') does not exercise the adapter. It swaps in a local fake, so endpoint, scheme, CNAME, and option handling never run. Keep at least one thin real-bucket integration test, because that is exactly the class of bug 2.8.1's scheme fix addressed — config resolution bugs are invisible to fakes.

Extension points are thin by design. The provider registers a oss driver via the Storage manager; if you need custom behaviour (per-tenant buckets, custom HTTP client options, signing tweaks) extend Illuminate\Filesystem\FilesystemManager in your own service provider or wrap Storage::disk() in a small repository class rather than subclassing the adapter. You can also register additional disks at runtime with Storage::build([...]), which is handy for multi-tenant apps where the bucket name comes from a model.

Keep an eye on the dependency chain. This package wraps Flysystem v3 plus the Aliyun SDK; a Laravel major bump (13) or PHP bump (8.5) is a signal to run your storage tests first, since the failure mode is usually a silent behaviour change in the adapter, not a loud exception.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource