luoyy/ali-oss-storage
luoyy/ali-oss-storage adds a ready-to-use Alibaba Cloud OSS filesystem driver for Laravel, letting you store, retrieve, and serve files through OSS with familiar Storage API calls. Configure your bucket credentials and start uploading with minimal setup.
luoyy/ali-oss-storage is a thin Laravel filesystem driver that registers an oss disk backed by Alibaba Cloud OSS. It's a community continuation of jacobcyl/ali-oss-storage now covering Laravel 10 through 13 (Laravel 13 support landed in v5.0.2), and the day-to-day mental model is: it's just a disk. If you know Storage::disk('local'), you know this package.
Minimal setup:
aliyuncs/oss-sdk-php transitively):
composer require luoyy/ali-oss-storage
.env:
ALIYUN_OSS_ACCESS_KEY_ID=...
ALIYUN_OSS_ACCESS_KEY_SECRET=...
ALIYUN_OSS_BUCKET=my-bucket
ALIYUN_OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
ALIYUN_OSS_DOMAIN= # optional CDN / custom domain
ALIYUN_OSS_IS_CNAME=false
config/filesystems.php:
'oss' => [
'driver' => 'oss',
'access_key_id' => env('ALIYUN_OSS_ACCESS_KEY_ID'),
'access_key_secret' => env('ALIYUN_OSS_ACCESS_KEY_SECRET'),
'bucket' => env('ALIYUN_OSS_BUCKET'),
'endpoint' => env('ALIYUN_OSS_ENDPOINT'),
'domain' => env('ALIYUN_OSS_DOMAIN'),
'is_cname' => env('ALIYUN_OSS_IS_CNAME', false),
],
Where to look first: the package is small — read src/ in the repo (Luoyy\AliOSS\AliOssAdapter and the service provider) before trusting your assumptions about which methods are implemented. It's faster than guessing from method signatures.
First use case — avatar upload returning a public URL:
$path = $request->file('avatar')->store('avatars', 'oss');
$url = Storage::disk('oss')->url($path);
That's the whole happy path. Everything below is about making that predictable at scale.
Keep the disk name as a variable, not a literal. The most common regression is tests or local dev writing to local while production writes to oss. Use config('filesystems.default') or a dedicated env (FILESYSTEM_DISK=oss), and reference Storage::disk($disk) everywhere. This one habit makes the driver switchable and testable.
Upload patterns by shape:
$request->file('x')->store('uploads/2024/06', 'oss')Storage::disk('oss')->put('reports/foo.pdf', $contents)->writeStream($path, fopen($tmp, 'r')) — avoid put for anything over a few MB.Storage::disk('oss')->makeDirectory($prefix) before bulk put calls; OSS is object-based, so this is a no-op for existence but keeps listing predictable.Presigned URLs for private objects. The adapter version matters — verify temporaryUrl() in source before leaning on it. If it's not there, fall back to calling the Aliyun SDK directly through a small service class rather than forking the adapter. A single method:
app(AliyunOssClient::class)->signUrl($bucket, $key, $expires);
CDN / custom domain. Set domain to your CDN host (e.g. cdn.example.com) and is_cname => true when the bucket is bound to that domain. Storage::url() will then emit CDN URLs — this is the correct place to wire up image resizing or cache-busting, not in views.
Queues and jobs. Any upload over ~5 MB should be a job. Store the file to local first (or a temp stream), dispatch ProcessUpload, and let the worker call the OSS disk. This keeps web requests fast and retries OSS flakiness for free.
Directory convention matters more than you think. OSS listings are prefix-based and paginated at 1000 objects. A flat bucket with UUID filenames is fine; a bucket with million-key directories is not. Use tenant/{id}/, uploads/{Y}/{m}/, or similar so allFiles($prefix) stays bounded.
Testing. Storage::fake('oss') swaps the disk to a local temp filesystem — your tests never hit Aliyun. Add one integration test (marked @group slow) that actually round-trips put/get/url against a sandbox bucket, because fake() will not catch signature or domain misconfigurations. If you're upgrading to Laravel 13, run that integration test there specifically — the framework-version bump is the only thing v5.0.2 changed, so it's the only thing that could have regressed.
oss-cn-hangzhou.aliyuncs.com, not the bucket-qualified my-bucket.oss-cn-hangzhou.aliyuncs.com and not the -internal variant unless you're inside the same VPC. Wrong endpoint → 301 Moved Permanently or SignatureDoesNotMatch.domain + is_cname must agree. A CDN domain without is_cname produces 403s; a bucket-bound CNAME with a public domain produces signature errors. This is the #1 debugging trap. Reproduce with ossutil ls oss://bucket using the same credentials before blaming the package.storePublicly() and visibility => 'public' only work if the adapter forwards the header and the bucket ACL allows it. If public URLs 403, set the bucket ACL or use signed URLs — don't assume the driver wrote an ACL.file-{uuid}.jpg) or call the CDN purge API.url() behavior depends on config. No domain → the adapter reconstructs <bucket>.<endpoint>/<path>. With domain → that host is used verbatim. If URLs look wrong, this is why.oss. It's whichever string the service provider registers via Storage::extend. Registering a disk with driver => 'aliyun' silently falls through to an InvalidArgumentException — check the provider source if you rename.allFiles($prefix) internally follows continuation tokens; on buckets with 100k+ objects this is slow and memory-hungry. Filter by prefix and date rather than listing everything.composer.json ("luoyy/ali-oss-storage": "~5.0.2") and re-test after upgrades. If the fork goes quiet, Storage::extend('oss', fn () => ...) in your AppServiceProvider lets you ship your own adapter without forking — this is the cleanest escape hatch.OssClient) or set OSS_DEBUG=1 in a throwaway command. The error code (InvalidAccessKeyId, SignatureDoesNotMatch, NoSuchBucket) tells you exactly which config knob is wrong./ is stripped; backslashes are not. Build keys with implode('/', ...) and never with Windows-flavored helpers.uploadFile, which the adapter may expose) or override the client timeout.Storage::extend registration and temporaryUrl on 13 in a staging run before trusting it in production.How can I help you explore Laravel packages today?