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

zgldh/qiniu-laravel-storage

Qiniu cloud storage driver for Laravel. Integrates Qiniu object storage with Laravel's filesystem, letting you upload, store and serve files through the familiar Storage facade. Supports custom domains, upload tokens and easy configuration via a dedicated filesystem disk.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the driver and publish its config:

composer require zgldh/qiniu-laravel-storage
php artisan vendor:publish --provider="zgldh\QiniuStorage\QiniuStorageServiceProvider"

On Laravel 5.5+ the service provider is auto-discovered. On 5.4 and older, register zgldh\QiniuStorage\QiniuStorageServiceProvider in config/app.php. The package registers a custom qiniu filesystem driver, so from then on everything goes through Laravel’s normal Storage API.

Set credentials in .env (names depend slightly on the published config, but the values are always the same four):

QINIU_ACCESS_KEY=...
QINIU_SECRET_KEY=...
QINIU_BUCKET=my-bucket
QINIU_DOMAIN=https://cdn.example.com   # CDN domain bound to the bucket

Then add a disk in config/filesystems.php:

'qiniu' => [
    'driver'     => 'qiniu',
    'access_key' => env('QINIU_ACCESS_KEY'),
    'secret_key' => env('QINIU_SECRET_KEY'),
    'bucket'     => env('QINIU_BUCKET'),
    'domain'     => env('QINIU_DOMAIN'),
],

Older releases of this package read a separate config/qiniu.php with accessKey/secretKey/bucket/domain and use a disk definition that only contains 'driver' => 'qiniu'. Open the file that vendor:publish actually produced before you start debugging.

First use case — avatar upload. This is the shortest path to proving credentials, bucket and CDN domain are all correct:

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

// smoke test in tinker
Storage::disk('qiniu')->put('healthcheck.txt', 'ok');
Storage::disk('qiniu')->exists('healthcheck.txt'); // true

If put succeeds but the URL 404s, the problem is almost always the domain or the bucket’s CDN binding, not the code.

Implementation Patterns

Single disk, configured as default where appropriate. Most projects add qiniu as a disk and either call Storage::disk('qiniu') explicitly or set FILESYSTEM_DRIVER=qiniu. Be explicit in application code — it makes local/test switching obvious.

Name files yourself, don’t trust client names. Qiniu keys are flat strings; there are no real directories, only prefixes. Use a deterministic, collision-free key and store it in the database:

$key = 'avatars/' . $user->id . '/' . Str::uuid() . '.' . $file->extension();
Storage::disk('qiniu')->putFileAs('', $file, $key);

Never store the full URL in the database — store the key and generate the URL at render time. Domains change, HTTPS gets enabled, and CDN domains get migrated.

Direct browser uploads for large files. Proxying large uploads through PHP is slow and burns memory. Use the package’s token helper server-side and the Qiniu JS SDK client-side:

$token = Storage::disk('qiniu')->getUploadToken([
    'scope'        => config('filesystems.disks.qiniu.bucket'),
    'deadline'     => time() + 600,
    'saveKey'      => 'uploads/$(year)$(mon)/$(etag)$(ext)',
    'returnBody'   => '{"key":"$(key)","hash":"$(etag)","size":$(fsize)}',
    'callbackUrl'  => route('qiniu.callback'),
    'callbackBody' => 'key=$(key)&hash=$(etag)&size=$(fsize)&uid=' . auth()->id(),
]);

If you use callbacks, verify them server-side with the Qiniu SDK (Auth::verifyCallback) rather than trusting the POST body.

Private buckets. url() produces an unsigned link that will be rejected on a private bucket. Generate signed links on demand with a short TTL:

$url = Storage::disk('qiniu')->privateDownloadUrl($key, 3600);

Return these through a controller or a view composer; never cache signed URLs past their expiry.

Image processing. Qiniu does image transforms at the CDN edge via query strings, not in the package. Wrap it:

function thumb(string $key, int $w = 200): string
{
    return Storage::disk('qiniu')->url($key) . "?imageView2/2/w/{$w}";
}

Same idea for imageMogr2, video frame capture, and avsc/vframe — all URL-based. Keep these helpers in one place so you can change the CDN domain in one shot.

Multi-bucket setups. Define multiple disks all using 'driver' => 'qiniu', each with its own bucket and domain (e.g. qiniu-public for avatars, qiniu-private for documents). This is cleaner than swapping config at runtime.

Media metadata. For audio/video, avInfo($key) returns duration, dimensions and bitrate, which is useful for validating uploads. getMetadata($key) gives size and mime type. Both hit the API — don’t call them in list loops.

Cleanup. Delete on model deletion via an observer, but tolerate failure (log and continue) — a failed Qiniu delete should not roll back a database transaction. Qiniu delete of a missing key returns an error; wrap it.

Testing. Storage::fake('qiniu') swaps in a local disk, so put/exists/assertExists work normally in feature tests. URL assertions will use the local fake domain, so assert on the key rather than the full URL.

Gotchas and Tips

  • Laravel 9+ compatibility is the biggest risk. Laravel 9 moved to Flysystem 3 and changed the internal FilesystemAdapter contract. This package’s last release is from 2022; verify it against your Laravel version before committing, and be prepared to pin Laravel 8 or maintain a fork. Run a put/url/delete cycle in a scratch project first — the failure mode is a driver not supported or a method signature error, not a clear message.
  • domain must be the CDN domain bound to the bucket, with a scheme and without a trailing slash. A source-storage domain (or the bucket’s test domain, which expires) yields 401/404s that look like upload failures. Some versions expect a domains array with a default key rather than a plain string — check the published config.
  • CDN caching + overwrite-in-place is a trap. Rewriting the same key serves stale bytes until the CDN refreshes. Either version keys (.../uuid.ext) or call the CDN refresh API after overwriting.
  • URL-encode keys containing spaces, +, # or Chinese characters. Qiniu keys are raw strings; the adapter does not consistently encode when building URLs. Normalize to ASCII-safe keys if you can.
  • Leading slashes create duplicate keys. /avatars/x.jpg and avatars/x.jpg are different objects in some code paths. Strip them at the boundary.
  • No real directories. allFiles()/directories() are emulated via prefix listing and are paginated (1000 per call). Avoid them in request paths; keep your own index table if you need listing.
  • Exceptions, not false. Network errors and API errors surface as Qiniu\Http\Error or SDK exceptions. Wrap uploads in try/catch and inspect the error code (612 = no such file, 614 = file exists). exists() costs a stat call; use it sparingly.
  • Server clock matters. Upload tokens and private URLs are HMAC-signed with a deadline. A server clock off by minutes produces confusing 401s. Keep NTP running.
  • Large uploads through PHP. file_get_contents() on a 200MB video will OOM before it reaches Qiniu. Use putFile/streams for moderate sizes and browser-direct uploads for anything large. There is no resumable upload on the server side.
  • Config caching. After changing credentials or domains, run php artisan config:clear. config:cache bakes env values in, which bites during deploys.
  • Extension points are thin. The clean approach is a small StorageService/repository class that wraps Storage::disk('qiniu') behind an interface, so you can swap backends and add cross-cutting concerns (logging, retries, key generation) without touching call sites. Don’t scatter Storage::disk('qiniu') calls across controllers.
  • Assume low maintenance. Pin the package version, keep the Qiniu PHP SDK locked, and budget a migration path (Laravel’s native S3 driver against a Qiniu S3-compatible endpoint, or a maintained fork) if the driver blocks a framework upgrade.
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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox