kamilz/b2backblaze
A Laravel package that integrates Backblaze B2 cloud storage as a filesystem driver, making it easy to upload, store, retrieve, and delete files through familiar Laravel Storage APIs with simple configuration.
Install the package and wire credentials into Laravel's config in three steps:
composer require kamilz/b2backblaze
B2_ACCOUNT_ID=xxxxxxxxxxxx
B2_APPLICATION_KEY=yyyyyyyyyyyy
B2_BUCKET_ID=zzzzzzzzzzzz
B2_BUCKET_NAME=my-app-bucket
config/b2.php and pull from env(). Never hard-code keys; Backblaze keys are long-lived and bucket-scoped, so leaking one exposes the whole bucket.AppServiceProvider (see Implementation Patterns) so the underlying B2\Client is reused per request.First use case: authorize and list buckets before touching files. This validates credentials without risking a half-upload:
$client = app(\B2\Client::class);
$buckets = $client->listBuckets();
// confirm $buckets contains your expected bucketId
The library follows the Backblaze B2 REST API closely (listBuckets, listFiles, uploadFile, deleteFile, getFileInfo, getDownloadAuthorization). There is no facade, no service provider shipped, and no Laravel-specific sugar — expect to pass Backblaze parameter arrays directly.
Look first at the README and the B2\Client class methods; the package is a thin wrapper, so the real API documentation is Backblaze's own b2_* endpoint reference.
Singleton binding in AppServiceProvider:
$this->app->singleton(\B2\Client::class, function () {
return new \B2\Client(
config('b2.account_id'),
config('b2.application_key'),
);
});
The client caches an auth token internally for ~24 hours; resolving it fresh each request wastes a round-trip. If you're on Octane, prefer a scoped binding or re-resolve when the token is near expiry.
Upload pattern (small files, < 100 MB):
$client->uploadFile([
'BucketId' => config('b2.bucket_id'),
'FileName' => "avatars/{$user->id}.png",
'Body' => $bytes,
'mimeType' => 'image/png',
]);
Backblaze requires SHA1 and Content-Length; the wrapper computes them, but double-check on binary streams — a mangled upload usually surfaces as a 401 on the second step, not the first.
Large files: the B2 API is two-step (get an upload URL → POST bytes). For anything over a few hundred MB, split into parts and uploadPart calls. The package exposes getUploadUrl and uploadPart, but you own the chunking and retry logic. Wrap it in a Job with $tries = 3 and exponential backoff.
Downloads: private buckets are the norm. Do not build URLs directly. Use:
$url = $client->getDownloadAuthorization([
'BucketId' => config('b2.bucket_id'),
'FileName' => $path,
'ValidDurationInSeconds' => 3600,
]);
// redirect to $url->downloadUrl
Cache these short-lived URLs and hand them to the browser; they expire and are safe to share briefly.
Deletion is versioned. deleteFile needs both FileName and FileId. Calling it with only a name will fail — always resolve the FileId via listFiles or store it alongside your DB record:
$file = $client->listFiles(['BucketId' => $id, 'Prefix' => $path, 'MaxFileCount' => 1]);
$client->deleteFile(['FileName' => $path, 'FileId' => $file[0]->fileId]);
Storage adapter: wrap the client in a small B2Storage class with put(), get(), exists(), url(). Keeping the raw client out of controllers makes it trivial to swap to the S3-compatible endpoint (Backblaze exposes one) if you later prefer Flysystem.
Testing: mock the B2\Client binding in your tests — the library makes real HTTP calls, and there is no fake shipped.
uploadFile throws an authorization error, the most common cause is an expired upload URL — URLs are short-lived (24h). Re-call getUploadUrl on retry, don't reuse the old one.listFiles returns the latest, but storage fills up if you only "delete" — use lifecycle rules or explicit FileId deletes.B2\Client holds state (auth token, upload URLs). A long-lived singleton across requests can serve a stale token. Add a health check or re-instantiate per worker cycle.503, not a clear message.php artisan config:cache snapshots .env. Changing B2_APPLICATION_KEY after caching silently uses the old key until you re-cache.503 SlowDown: Backblaze throttles per-account. Wrap mutating calls in a retry with jitter; queue heavy uploads and cap concurrency per bucket.B2Storage service class or a Retryable trait. If you need more, note that Backblaze's S3-compatible API lets you drop in league/flysystem-aws-s3-v3 and get the whole Laravel Storage facade for free; only reach for this package when you need B2-native features (file versions, specific bucket IDs, cheap egress reporting).composer.json and budget a fork or migration path.How can I help you explore Laravel packages today?