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

Qiniu Php Sdk Laravel Package

wolfcode/qiniu-php-sdk

A PHP SDK integration for Qiniu cloud storage that simplifies configuring credentials and working with Qiniu services such as object storage and file uploads in PHP and Laravel projects. It streamlines client setup and common operations.

View on GitHub
Deep Wiki
Context7

Getting Started

Verify what you're actually pulling in first. This package has zero stars and zero dependents, which means it is almost certainly a repackaging or fork of the official qiniu/php-sdk. That was true at v0.1.2 and it's even more obvious at v8.0.1 — the maintainers jumped the minor/major to line up with upstream qiniu/php-sdk 8.x rather than continuing their own 0.x sequence. Before writing code:

composer show --all wolfcode/qiniu-php-sdk      # check requires: php, ext-*, guzzle, etc.
composer show wolfcode/qiniu-php-sdk            # what you actually installed

Then diff the vendor tree against qiniu/php-sdk:^8.0:

diff -r vendor/wolfcode/qiniu-php-sdk/src vendor/qiniu/php-sdk/src

If the public API surface (Qiniu\Auth, Qiniu\Storage\UploadManager, Qiniu\Storage\BucketManager, Qiniu\Config) is identical — and it likely is — treat all upstream 8.x documentation as authoritative and read this assessment as a guide to that API. The version jump also means there is no 7.x → 8.x migration history in this repo: the fork skipped straight to 8, so any upstream 8.0 breaking changes apply to you even though your installed version only "went up" by a patch number relative to 0.1.2.

Install and configure:

composer require wolfcode/qiniu-php-sdk:^8.0

Check the php constraint in composer.json before you commit — upstream 8.x dropped support for older PHP runtimes. If you're on an EOL PHP version, composer require will either fail or silently resolve you back to 0.1.x.

Publish a config block (Laravel config/services.php is fine for a single bucket):

'qiniu' => [
    'access_key' => env('QINIU_ACCESS_KEY'),
    'secret_key' => env('QINIU_SECRET_KEY'),
    'bucket'     => env('QINIU_BUCKET'),
    'domain'     => env('QINIU_DOMAIN'), // e.g. https://cdn.example.com
    'private'    => env('QINIU_PRIVATE', false),
    'region'     => env('QINIU_REGION', 'z0'), // see gotchas — set this explicitly
],

First use case — upload a file and get a public URL:

use Qiniu\Auth;
use Qiniu\Storage\UploadManager;

$auth  = new Auth(config('services.qiniu.access_key'), config('services.qiniu.secret_key'));
$token = $auth->uploadToken(config('services.qiniu.bucket'));

[$result, $err] = (new UploadManager())->putFile(
    $token,
    'uploads/'.Str::uuid().'.jpg',
    $request->file('photo')->getRealPath()
);

if ($err !== null) {
    throw new RuntimeException($err->message());
}

$url = config('services.qiniu.domain').'/'.$result['key'];

That five-line flow — Auth → uploadToken → UploadManager → $result['key'] — is the whole core of the SDK. Everything else is variations on it. Confirm it still works after the upgrade (see the smoke-test note in Gotchas); this is the exact snippet to run first.

Where to look first: the Qiniu\Auth class for token generation and URL signing, Qiniu\Config for region/zone and HTTP settings, and Qiniu\Storage\UploadManager / Qiniu\Storage\BucketManager for operations. The Qiniu\Http\Error class is what you'll be inspecting in every failure path — verify its accessors on 8.x before relying on message() / code() / getResponse().

Implementation Patterns

Wrap it in a single service, not scattered calls. The SDK is stateless and procedural; every call site constructs an Auth and a manager. Centralize it:

final class QiniuStorage
{
    private Auth $auth;
    private string $bucket;

    public function __construct(array $config)
    {
        $this->auth   = new Auth($config['access_key'], $config['secret_key']);
        $this->bucket = $config['bucket'];
    }

    public function upload(string $key, string $localPath): string
    {
        $token = $this->auth->uploadToken($this->bucket);
        [$res, $err] = (new UploadManager())->putFile($token, $key, $localPath);

        if ($err !== null) {
            throw new QiniuException($err->message(), $err->code(), $err);
        }

        return $res['key'];
    }
}

Bind it as a singleton in a service provider. This is the seam you'll mock in tests — and it's also your insulation against the next version jump in this fork.

Return the tuple, then throw once. Every SDK method returns [$result, $error] rather than throwing. Don't propagate that shape into your application layer — collapse it at your service boundary, as above. A QiniuException wrapping Qiniu\Http\Error keeps the original code and response body available for logging.

Use directory-prefixed, UUID-based keys. Qiniu has no real directories; the key is the path. uploads/2025/07/{uuid}.{ext} gives you prefix-based listing, per-tenant isolation, and no collisions:

$key = sprintf('uploads/%s/%s.%s', now()->format('Y/m'), Str::uuid(), $ext);

Never derive keys from user-supplied filenames — you'll get encoding issues, collisions, and path traversal in downstream tooling.

Private buckets: sign URLs, don't proxy. If the bucket is private, don't stream files through your app. Generate a signed URL with an expiry:

$url = $this->auth->privateDownloadUrl(
    config('services.qiniu.domain').'/'.$key,
    3600
);

Return that to the frontend and let it hit the CDN directly. This is the single biggest performance decision in a Qiniu integration.

Batch operations for anything list-shaped. BucketManager::batch() takes an array of single-op calls (stat, delete, copy, move) and executes them in one request, capped at 1000 ops. Looping delete() over 500 keys is 500 round trips and will time out in a queue worker.

$bm = new BucketManager($this->auth);
$ops = collect($keys)->map(fn ($k) => BucketManager::delete($this->bucket, $k))->all();
[, $errs] = $bm->batch($ops);

Note the partial-failure semantics: $errs is a map of index → error, and the successful ops still executed. Handle it as a partial success, not all-or-nothing.

Processing pipelines go through persistent fops. Watermarks, thumbnails, and video transcodes should run server-side at Qiniu, not in your PHP process:

$bm->persistentOps(
    'avthumb/mp4/s/640x360',
    $key,
    'videos/processed/'.basename($key),
    $this->bucket,
    true // use HTTPS
);

Then watch for the callback or poll with BucketManager::stat(). Fired-and-forgotten fops with no completion tracking is a common source of "the thumbnail never appeared" bugs.

Upload tokens should be scoped and short-lived. The default uploadToken($bucket) is broad. For browser/direct-upload flows, pass an options array with scope (bucket:key), insertOnly (prevents overwrites — worth setting on every upload), expires, returnBody, and callbackUrl + callbackBody. Verify the callback with $auth->verifyCallback(...) before trusting the payload; the callback endpoint is public internet. Re-check this method's signature against the 8.x source — it's one of the more likely places for a breaking change.

Laravel Filesystem adapter is worth it for read-heavy apps. There's a community league/flysystem-qiniu adapter. If your code is Storage::disk('qiniu')->put(...), an adapter gives you put, get, url, exists, delete uniformly. But the adapter covers a subset — batch ops, fops, and upload callbacks all need the raw SDK. Use the adapter for simple paths, fall through to your QiniuStorage service for everything else. Pin the adapter version deliberately: a Flysystem v1/v3 mismatch will bite harder than anything in the SDK itself.

Gotchas and Tips

Pin the version and audit the diff. Zero dependents and a recent release date mean this fork has essentially no production track record — and 0.1.2 → 8.0.1 is a version-number reset, not a track record. Lock to an exact version in composer.json, commit composer.lock, and re-diff against upstream qiniu/php-sdk on every upgrade. If the fork adds nothing you need, use the upstream package — you get the same code with an actual security-response history and real release notes instead of a bare changelog link.

The changelog is a compare URL, not release notes. v8.0.1 ships literally one line: a GitHub compare link from v0.1.2. That tells you nothing about breaking changes. Treat the upgrade as a major-version migration: run your smoke test (Auth → uploadToken → putFile → delete) against a dev bucket, and grep your codebase for anything touching Error, verifyCallback, persistentOps, and BucketManager::batch before deploying.

Smoke test before you trust the upgrade. Minimal script, one assertion per line:

[$r, $e] = (new UploadManager())->putFile($token, 'smoke/'.Str::uuid().'.txt', $tmp);
assert($e === null);
[, $e2] = (new BucketManager($auth))->delete($bucket, $r['key']);
assert($e2 === null);

If Error gained typed accessors or changed its constructor in 8.x, your QiniuException wrapper will break here rather than in production.

Auth construction is cheap but not free — and the region matters. Every Auth you new does work; construct it once per request via the container. More importantly, uploads need to resolve the correct region/zone (East China z0, North China z1, South China z2, US, Southeast Asia, etc.). Older SDK versions auto-probed the region and cached it; newer ones read Qiniu\Config and can fail with cryptic incorrect region errors when the bucket lives somewhere unexpected. If you see that error, set the zone explicitly (new Config() + $config->useRegion(...), or pass the zone into Auth) rather than relying on auto-detection. On 8.x, confirm which of these constructors is still public before wiring it into your service provider.

Error objects aren't exceptions. $err->message() gives you the message, $err->code() the HTTP-ish code, but the genuinely useful part is often in $err->getResponse()->body. Log the whole thing. A bare if ($err) { return false; } will cost you hours when production starts behaving oddly.

Silent-success traps. putFile on a path that doesn't exist, or with a token scoped to a mismatched key, can fail in ways that look like success if you don't check $err. Always check the tuple explicitly — the SDK never throws on HTTP failures.

Private-bucket URL expiry is baked in. A signed URL that's cached anywhere (browser, CDN, your own response cache) stops working the moment it expires. Either keep expiry comfortably longer than any caching layer, or sign at request time and never cache the signed URL itself.

putFile vs put for large files. putFile reads the whole file into memory in most versions. For anything over ~10–20 MB, use the resumable path or upload from a stream. Also watch PHP's memory_limit and upload_max_filesize — a "Qiniu upload failed" is sometimes just PHP refusing the temp file before the SDK ever runs.

Callbacks need idempotency and verification. Qiniu retries callbacks. Your callbackUrl endpoint must be idempotent (key on the file key or a request ID) and must call verifyCallback with the configured token before touching the database. An unverified callback endpoint lets anyone write rows into your table.

Extension points are thin, so keep your own boundary. The SDK has no pluggable middleware, no PSR-18/PSR-7 HTTP client abstraction, and no event hooks. You can't intercept requests cleanly. Don't try — the practical extension point is the wrapper class you write around it. Keep the SDK's types (Auth, UploadManager, Error) out of your controllers and jobs; let only your QiniuStorage (or an interface behind it) leak into application code. This is also what makes the next surprise version bump survivable.

Testing. There's no fake or in-memory transport. For unit tests, mock your QiniuStorage interface. For integration tests, use a dedicated dev bucket with a short-lived token and insertOnly, and clean up with a batch delete in tearDown. Avoid hitting the real API in CI runs that gate merges — flaky network plus a third-party service equals red builds nobody trusts. Run integration tests on a scheduled job against the pinned version instead.

Queue your uploads when they're user-facing. The SDK is synchronous and blocking. A 5 MB upload on a request that also renders a page will feel slow. Hand the temp file to a job, upload there, and update the model when it lands — you already have the queue.

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.
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
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons