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

Backblaze B2 Laravel Package

gliterd/backblaze-b2

Laravel package that integrates Backblaze B2 cloud storage with the framework's filesystem, so you can upload, store, and serve files through a familiar driver interface with simple configuration and the official B2 SDK.

Deep Wiki
Context7

Getting Started

Install and wire it up in three steps:

composer require gliterd/backblaze-b2
php artisan vendor:publish --provider="Gliterd\BackblazeB2\BackblazeB2ServiceProvider"

Then set credentials in .env (names vary slightly by version — confirm against config/backblaze.php after publishing):

BACKBLAZE_B2_ACCOUNT_ID=your_account_id
BACKBLAZE_B2_APPLICATION_KEY=your_application_key

The package ships a service provider and a BackblazeB2 facade, so a smoke test is one line from php artisan tinker:

use Gliterd\BackblazeB2\Facades\BackblazeB2;

BackblazeB2::listBuckets(); // confirms auth, config, and HTTP stack all work

Where to look first: the package predates good docs. Read src/Client.php (or the equivalent top-level class) first — it maps 1:1 to Backblaze's B2 REST API (b2_authorize_account, b2_list_buckets, b2_get_upload_url, etc.). The bucket and file models next to it are thin wrappers. The README is worth 5 minutes; the source is worth 30.

First realistic use case: offloading user-generated media (avatars, PDFs, exports) from local disk or S3 to B2 because storage cost is the driver. Start with getBucket() + an upload path, not with a full Storage integration — B2 has no first-class Laravel disk driver in this package.

$bucket = BackblazeB2::getBucket('user-uploads');
$file = $bucket->upload('avatars/42.png', fopen($path, 'r'));
// persist $file->getId() alongside the filename

Implementation Patterns

Treat B2 file IDs as the primary key. B2 deletes require both the fileId and the fileName; the SDK models expose both. Store them together in your database rather than reconstructing paths later:

// migration
$table->string('b2_file_id')->nullable();
$table->string('b2_file_name')->nullable();

Wrap the facade in a small service and inject it. The facade is convenient but painful to fake and hides the account/bucket coupling. A one-class wrapper (App\Storage\B2Storage) gives you a single seam for tests, retries, and swapping providers later:

class B2Storage
{
    public function __construct(private string $bucketName) {}

    public function put(string $path, $stream, array $options = []): array
    {
        $bucket = BackblazeB2::getBucket($this->bucketName);
        $file   = $bucket->upload($path, $stream, $options);

        return ['id' => $file->getId(), 'name' => $file->getName()];
    }
}

Bind it in a service provider, resolve it from the container, and you can $this->mock(B2Storage::class) in feature tests without touching HTTP.

Upload from a queued job, not the request cycle. Uploads are network-bound and occasionally retried; the request should only enqueue:

class PushAvatarToB2 implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(B2Storage $storage): void
    {
        $storage->put("avatars/{$this->user->id}.png", fopen($this->localPath, 'r'));
    }
}

List with prefix + pagination. listFiles() takes a bucket and filters; for anything beyond a few hundred objects you must page with the last returned filename as startFileName. Don't loop listFiles() with no cursor — you'll silently cap at the API's default page size.

Use it for backups, not just user files. A scheduled command that tars a database dump and pushes it with a dated path (backups/2024/06/01.sql.gz) is the lowest-effort, highest-value use of the package. Keep the last N file IDs in a table so a prune job can delete the old ones.

Integration tip — pair with Flysystem if you need Storage::. B2 speaks the B2 API, not S3, so Laravel's s3 driver won't work. Either (a) keep this package behind your own service as above, or (b) add a Flysystem B2 adapter and register a custom disk. Mixing both leads to two auth paths and confused config.

Gotchas and Tips

  • Age is the biggest gotcha. Last release was January 2019, targeting the Laravel 5.x / PHP 7.x / Guzzle 6 era. On Laravel 9+ / PHP 8.2 you will likely hit composer conflicts; budget for a fork (repositories + vcs entry) or pick a maintained B2 client. Check composer.json constraints before you commit to it.
  • Auto-discovery may not apply. On Laravel ≤5.4 (and any version where the manifest is cached oddly) register Gliterd\BackblazeB2\BackblazeB2ServiceProvider and alias BackblazeB2 in config/app.php manually.
  • config:cache will bite you. Env-only credentials read through env() outside of config files return null once cached. Always put env() calls in config/backblaze.php and read config('backblaze.…') everywhere else.
  • Auth tokens expire (~24h). The client authorizes per process. That's fine for web requests, fatal for long-running queue workers and Octane, which will keep a stale token. Add a re-authorize-on-401 wrapper, or keep workers short-lived.
  • Upload URLs expire too, and get invalidated (~503). A robust upload path needs retry-with-fresh-upload-URL logic. If you see intermittent 5xx on uploads under load, that's this — not your app.
  • Large files. B2 requires multipart ("large file") uploads past a configurable threshold; the simple upload() path is single-shot. Verify which method handles multipart in the source, and never read a multi-hundred-MB file into a string — pass a stream/filename.
  • File names are case-sensitive, arbitrary UTF-8, and can contain /. URL-encode when constructing paths or download URLs; don't assume basename() round-trips.
  • Bucket privacy is set at creation. allPrivate vs allPublic — if you need signed, time-limited downloads, confirm the SDK exposes b2_get_download_authorization; if it doesn't, you'll be adding that call yourself.
  • No built-in retry/backoff. Guzzle is underneath, so pass a custom handler or middleware stack to the client for logging and retry on 429/5xx. This is the cleanest extension point: swap the HTTP client, keep the SDK models.
  • Testing: mock at the Guzzle handler level (MockHandler) if you're exercising the client directly, or fake your own service wrapper if you're not. The package's own tests are a decent crib sheet for real API shapes.
  • Debugging tip: log raw response bodies on any non-2xx. B2 returns a terse {"code": "...", "message": "..."} and the SDK's exceptions occasionally lose the message — you'll spend an hour on "unauthorized" caused by a typo'd key.
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.
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge