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 Cos Laravel Package

overtrue/flysystem-cos

Flysystem adapter for Tencent Cloud COS (Cloud Object Storage). Plug Tencent COS into League Flysystem and Laravel's filesystem so you can read, write, and manage remote objects using the familiar Storage API.

Deep Wiki
Context7

Getting Started

overtrue/flysystem-cos is a thin Flysystem adapter over Tencent Cloud COS (the SDK is qcloud/cos-sdk-v5). It gives you the standard Flysystem API — write, read, delete, listContents, streams, URLs — against a COS bucket. It is not a Laravel package by itself; Laravel users normally pair it with the overtrue/laravel-filesystem-cos bridge or register a custom disk driver.

Install and check the version matrix first. The package follows Flysystem majors. Current releases (^5.x) target Flysystem 3; older majors target Flysystem 1/2. Verify the league/flysystem constraint in composer.json before installing.

composer require overtrue/flysystem-cos

Minimal plain-PHP setup:

use League\Flysystem\Filesystem;
use Overtrue\Flysystem\Cos\CosAdapter;

$adapter = new CosAdapter([
    'region' => 'ap-guangzhou',
    'bucket' => 'my-bucket-1250000000', // full bucket name including APPID
    'credentials' => [
        'secretId'  => getenv('COS_SECRET_ID'),
        'secretKey' => getenv('COS_SECRET_KEY'),
    ],
    'domain' => 'https://cdn.example.com', // optional, for URL generation
]);

$fs = new Filesystem($adapter);

$fs->write('healthcheck.txt', 'ok');
var_dump($fs->fileExists('healthcheck.txt')); // true
echo $fs->publicUrl('healthcheck.txt');
$fs->delete('healthcheck.txt');

Laravel setup — either install the bridge, or register the driver yourself:

// AppServiceProvider::boot()
use Illuminate\Filesystem\FilesystemAdapter;
use League\Flysystem\Filesystem as Flysystem;
use Overtrue\Flysystem\Cos\CosAdapter;

Storage::extend('cos', function ($app, $config) {
    $adapter = new CosAdapter([
        'region' => $config['region'],
        'bucket' => $config['bucket'],
        'credentials' => [
            'secretId'  => $config['credentials']['secretId'],
            'secretKey' => $config['credentials']['secretKey'],
        ],
        'domain' => $config['domain'] ?? null,
    ]);

    return new FilesystemAdapter(new Flysystem($adapter), $adapter, $config);
});

Where to look first:

  1. composer.json — Flysystem major compatibility.
  2. src/CosAdapter.php — the real supported surface (URL generation, visibility, streams, getClient() escape hatch).
  3. qcloud/cos-sdk-v5 docs — region names, bucket naming, ACLs, signed URLs.
  4. The Laravel bridge README if you want zero driver plumbing.

First real use case: user uploads (avatars, documents) written to a prefix and served through a CDN domain or short-lived signed URLs, with listContents() used for simple inventory/cleanup jobs.

Implementation Patterns

Use one disk per environment, prefixes per tenant/type. Bucket-per-environment (app-prod-125..., app-staging-125...) keeps IAM and lifecycle rules simple. Inside a bucket, namespace aggressively: tenants/{id}/avatars/, exports/{date}/, backups/. COS is flat, so prefixes are your only structure.

Standard CRUD through Laravel Storage:

Storage::disk('cos')->put('reports/2024/q1.pdf', $contents);
Storage::disk('cos')->putFileAs('avatars', $request->file('avatar'), "{$userId}.png");
$contents = Storage::disk('cos')->get('reports/2024/q1.pdf');
Storage::disk('cos')->delete('reports/2024/q1.pdf');

Stream large files rather than buffering into strings:

$stream = fopen($localPath, 'r');
$fs->writeStream('backups/db.sql.gz', $stream);
fclose($stream);

$read = $fs->readStream('backups/db.sql.gz');
// stream_copy_to_stream(...)
fclose($read);

URL strategy: CDN for public, signed for private. Configure domain when assets are publicly served through CDN; otherwise the adapter falls back to the bucket's COS endpoint. For private objects use temporaryUrl():

$public  = Storage::disk('cos')->url('avatars/42.png');              // CDN URL
$private = Storage::disk('cos')->temporaryUrl(
    'invoices/42.pdf',
    now()->addMinutes(30)
);

Listing and reconciliation jobs. Flysystem 3 returns an iterable DirectoryListing; the SDK handles pagination:

foreach ($fs->listContents('exports/2024/', true) as $item) {
    if ($item->isFile()) {
        // $item->path(), $item->fileSize(), $item->lastModified()
    }
}

Always pass a narrow prefix. Recursive listing of a huge bucket is slow and billed per request.

Upload options and metadata. Pass per-write config where the adapter supports forwarding to putObject; check CosAdapter::write() for exactly which keys survive. Setting ContentType and visibility explicitly is safer than relying on extension guessing, especially for extensionless keys.

$fs->write('assets/logo', $bytes, [
    'ContentType' => 'image/svg+xml',
    'visibility'  => 'public',
]);

Escape hatch to the SDK. This adapter is intentionally thin. For multipart upload tuning, object tagging, batch deletes, inventory, or COS CI (image processing) parameters, drop to the underlying client:

$client = $adapter->getClient(); // confirm method name in your installed version
$client->putObject([...]);

Keep that code isolated in a small service class so the rest of the app stays on Flysystem.

Presigned direct uploads. The adapter generates read URLs; it does not implement browser-side upload orchestration. If you need direct-to-COS uploads (large video, mobile), generate presigned PUT URLs with the SDK and only use the adapter for subsequent reads/deletes.

Testing. Unit-test application code against League\Flysystem\InMemory\InMemoryFilesystemAdapter; mock the COS client for adapter-level tests. Run a small integration suite against a dedicated test bucket in CI, gated behind env vars, and clean up by prefix.

Config hygiene. Keep credentials in .env, use a CAM sub-account scoped to the bucket/prefix, and remember php artisan config:cache freezes values — never call env() outside config files.

Gotchas and Tips

  • Bucket name must include the APPID (my-bucket-1250000000). Passing only my-bucket produces confusing 404/signature errors. Some Laravel bridges split bucket + app_id; the core adapter expects the combined string.
  • Region must match the bucket, not the user's location (ap-guangzhou, ap-shanghai, ap-beijing, ...). A wrong region usually shows up as an endpoint/signature error, not a clean "bucket not found."
  • domain needs a scheme and no trailing slash. cdn.example.com or https://cdn.example.com/ can produce malformed or double-slashed URLs depending on version.
  • CDN and signed URLs are different trust models. A temporaryUrl() signature is computed for the COS host. Rewriting it to a CDN hostname breaks the signature unless the CDN is configured with origin authentication. Use the COS endpoint for signed links, or CDN signed URLs separately.
  • Directories are virtual. createDirectory() may be a no-op or zero-byte marker; has('some/dir') is not a reliable directory check. Use directoryExists() or list by prefix.
  • deleteDirectory() can be very slow and expensive on large prefixes because it must enumerate and delete objects. For bulk expiry, use COS lifecycle rules instead of application code.
  • Visibility is ACL-based and often overridden by bucket policy. Visibility::PUBLIC may fail or be effectively ignored if the bucket blocks public access; conversely a "private" object served through an open CDN is still publicly reachable. Treat ACL and CDN configuration as one decision.
  • Error types differ by layer. The SDK throws Qcloud\Cos\Exception\*; Flysystem wraps failures in UnableToWriteFile, UnableToReadFile, etc.; Laravel's put() returns false unless you use throw. Catch at the layer you actually call, and log the COS RequestId from the exception — it's what support asks for.
  • MIME types can end up as application/octet-stream, particularly for extensionless keys or uploads routed through putFileAs. Set ContentType explicitly for anything browsers will render.
  • Special characters and Unicode keys. COS keys are case-sensitive and URL encoding matters. Verify url() output for spaces, +, #, and CJK filenames before shipping; encode at the presentation layer if needed.
  • listContents() cost and latency. Every page is an API call. Never list recursively on a hot request path; cache inventories or store metadata in your database at write time.
  • Last-write-wins, no locking. Concurrent writes to the same key silently overwrite. If you need coordination, use versioned keys or a lock outside COS.
  • Flysystem 2 vs 3 API drift. Return types (void vs bool), exception classes, and publicUrl()/temporaryUrl() availability changed. Copying snippets from old blog posts is a common source of "method does not exist" errors.
  • Package maturity. At ~80 stars this is a small, single-maintainer adapter. It covers the common path well, but niche features (multipart copy, checksums, versioning, object lock) are likely absent. Read CosAdapter.php before promising a feature, and pin the version in composer.json.
  • No service provider in the core package. Laravel auto-discovery will not register a cos disk for you; use the bridge or the Storage::extend pattern above. Also watch for league/flysystem version conflicts with other adapters via composer why-not league/flysystem.
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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment