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

Flysystem Qcloud Cos V5 Laravel Package

freyo/flysystem-qcloud-cos-v5

A Flysystem v5 adapter for Tencent Cloud COS (Cloud Object Storage), letting Laravel and PHP apps store, read, and manage files on QCloud COS through the familiar Flysystem and Laravel Storage APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

freyo/flysystem-qcloud-cos-v5 is a Flysystem driver for Tencent Cloud COS (Object Storage), with a Laravel service provider that registers a cosv5 disk driver. Version 2.2.1 is a compatibility release: it widens the framework constraints to include Laravel 9 (illuminate/support ^9.0). There are no API changes, so this is a safe upgrade from 2.2.0 — but it is the minimum version to use if you're on Laravel 9.

Minimal setup:

  1. Install the SDK-backed adapter

    composer require freyo/flysystem-qcloud-cos-v5:^2.2
    

    Laravel's package discovery registers Freyo\Flysystem\QcloudCosV5\ServiceProvider, which hooks into the Storage facade via Storage::extend().

  2. Add credentials to .env (COS is region-scoped, and the bucket name must include the APPID suffix):

    COS_REGION=ap-guangzhou
    COS_APP_ID=1250000000
    COS_SECRET_ID=AKID...
    COS_SECRET_KEY=...
    COS_BUCKET=my-bucket-1250000000
    COS_CDN=            # optional custom/CDN domain
    
  3. Register a disk in config/filesystems.php. Check the package README for the exact key names for your installed tag — the credentials block is camelCase in most 2.x releases and is passed almost verbatim to the COS SDK:

    'cosv5' => [
        'driver'      => 'cosv5',
        'region'      => env('COS_REGION'),
        'credentials' => [
            'appId'     => env('COS_APP_ID'),
            'secretId'  => env('COS_SECRET_ID'),
            'secretKey' => env('COS_SECRET_KEY'),
        ],
        'bucket' => env('COS_BUCKET'),
        'cdn'    => env('COS_CDN'),
    ],
    
  4. First use case — a private bucket holding user uploads, served through short-lived signed URLs:

    Storage::disk('cosv5')->putFileAs('avatars', $request->file('avatar'), "{$user->id}.jpg");
    $url = Storage::disk('cosv5')->temporaryUrl("avatars/{$user->id}.jpg", now()->addMinutes(30));
    

Where to look first: the package's ServiceProvider (how the driver key is named) and its Adapter class (which Flysystem capabilities are actually implemented — temporaryUrl, visibility, mimetype are the ones that bite).

Implementation Patterns

Treat COS as a normal Laravel disk. Everything built on the Filesystem contract — putFileAs, store, download, exists, deleteDirectory — works, so existing code written against local or s3 generally ports by changing the disk name. Keep the disk name in config (FILESYSTEM_DISK=cosv5) so dev can fall back to local without touching call sites.

Split public and private disks. Most teams register two disks pointing at two buckets (or two prefixes): one with a CDN domain for assets, one private for documents. Public asset paths then use ->url(), private ones always use ->temporaryUrl().

Stream large files, never put() them. For anything above a few MB, use streams so the COS SDK can multipart-upload:

$stream = fopen($request->file('video')->getRealPath(), 'r');
Storage::disk('cosv5')->writeStream("videos/{$id}.mp4", $stream);

Validate then persist; never trust the client filename. store() keeps the original extension. Prefer storeAs with a generated name (Str::uuid()), and store the COS key in your database rather than a full URL — the URL changes when you switch CDN domains or sign with a different expiry.

Serve private objects through a controller rather than a signed URL when you need authorization logic, audit logging, or per-user rate limits:

Route::get('/files/{document}', function (Document $document) {
    abort_unless(Gate::allows('view', $document), 403);

    return Storage::disk('cosv5')->response($document->key);
});

Note that ->response() streams through PHP, so it consumes app bandwidth; for large media use temporaryUrl() and redirect instead.

Model attachment trait. A small trait that hides the disk name is worth the ten minutes:

public function fileUrl(): ?string
{
    return $this->key
        ? Storage::disk($this->disk ?? 'cosv5')->temporaryUrl($this->key, now()->addMinutes(15))
        : null;
}

Testing. Storage::fake('cosv5') swaps in a local driver and works with this package because it lives in Laravel's FilesystemManager, not the adapter itself — so you get Storage::disk('cosv5')->assertExists(...) in feature tests with no network calls. Keep at least one real integration test (or a CI job) that touches the bucket, gated on COS_SECRET_ID being present, to catch credential/region drift.

CI/CD and config caching. php artisan config:cache is mandatory in production; every env() call must live inside config/filesystems.php, never in a controller. Because the disk config is a plain array, you can override it per-environment without touching the package.

Gotchas and Tips

  • Laravel 9 / Flysystem 3 is the thing to verify on upgrade. Laravel 9 ships league/flysystem ^3.0, and Laravel's FilesystemManager only accepts Flysystem 3 adapters. 2.2.1 removes the framework constraint blocker, but before rolling it out, run one real read/write against the bucket on a Laravel 9 host — a passing composer install is not proof that the adapter registered correctly. If the driver key isn't found you'll get InvalidArgumentException: Driver [cosv5] is not supported, or a class-level League\Flysystem\FilesystemInterface not-found fatal.

  • 403 SignatureDoesNotMatch is almost always the secret key, not the bucket. COS signs with secretId/secretKey + region + bucket; a stray trailing space or an AKID prefix pasted into the wrong variable produces this. 404 NoSuchBucket usually means the bucket name is missing the -1250000000 APPID suffix or the region doesn't match the bucket's actual region.

  • url() on a private bucket returns a link that 403s. The adapter builds the URL; it does not sign it. If cdn is configured, url() returns the CDN host — which is why a "working in staging, broken in prod" URL bug is usually a COS_CDN difference between environments, or a CDN that hasn't been granted read access to the bucket.

  • Verify temporaryUrl() exists on your version before you build around it. Flysystem's contract includes it, but custom adapters sometimes stub it out; you'll get RuntimeException: This driver does not support creating temporary URLs. Test it explicitly, and keep a controller-proxy fallback.

  • Visibility is coarse and often ignored. COS object ACLs are set at write time via headers; setVisibility() may be a no-op on some releases, and bucket-level ACL usually wins. Don't design a feature that flips individual objects public/private — use separate buckets.

  • Don't Storage::disk('cosv5')->allFiles() on a large bucket. Directory listing is paginated by the SDK but Flysystem may aggregate, and each request costs. Keep a database index of keys, or iterate with ListObjectsV2 directly through the SDK for admin/reporting tasks.

  • Exceptions aren't framework exceptions. The adapter throws League\Flysystem\UnableToWriteFile, UnableToReadFile, FilesystemException, etc. — not Laravel's FileNotFoundException. Catch the Flysystem interfaces if you need graceful degradation; uncaught, these surface as 500s from a queue job.

  • Extension points worth knowing. If you need custom SDK options (storage class, server-side encryption, custom headers), the disk config's options array is usually forwarded to the SDK — check the adapter source for which keys it merges. For anything deeper (custom URL resolution, cache headers), subclass the adapter and re-register the driver in your own service provider with Storage::extend('cosv5', ...), keeping the package as a dependency rather than forking it.

  • Upgrade path. 2.2.1 is additive: from 2.2.0 it is a patch-level bump with no migrations, config changes, or deprecations. Pin ^2.2.1 rather than ^2.2 if you depend on Laravel 9, since 2.2.0 will resolve on Composer and then fail at the framework constraint.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource