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

esac/backblaze-b2

Laravel package for integrating Backblaze B2 cloud storage, offering a simple API to upload, download, list, and manage files in B2 buckets. It wraps Backblaze B2's native API for use within Laravel applications and storage workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require esac/backblaze-b2

There is no Laravel auto-discovery, no service provider, and no facade. Expect to new the client yourself.

First place to look

  • README.md — likely thin; treat method names as provisional.
  • src/ — the client class is the whole API surface. Grep for function to enumerate authorizeAccount, getUploadUrl, uploadFile, listFileNames, deleteFileVersion, getDownloadUrl, etc.
  • composer.json — check the PHP constraint and whether it depends on Guzzle or raw cURL. This determines how easily you can fake it in tests.

Config you actually need Get these from the Backblaze B2 console under App Keys:

  • applicationKeyId
  • applicationKey (secret — never commit; put in .env)
  • bucketId (the ID, not the bucket name)

.env

B2_APPLICATION_KEY_ID=
B2_APPLICATION_KEY=
B2_BUCKET_ID=
B2_BUCKET_NAME=

First use case: upload a file and get a URL back

use Esac\BackblazeB2\Client; // verify namespace in src/

$client = new Client();
$client->authorizeAccount(env('B2_APPLICATION_KEY_ID'), env('B2_APPLICATION_KEY'));

$upload = $client->getUploadUrl(env('B2_BUCKET_ID'));
$file = $client->uploadFile(
    $upload['uploadUrl'],
    $upload['authorizationToken'],
    'invoices/2024/0001.pdf',
    file_get_contents($path),
    'application/pdf'
);

// $file['fileId'], $file['fileName'] -> persist in DB

That round trip — authorize, get upload URL, upload, store fileId — is the 90% case. Everything else in the SDK is variations on it.

Implementation Patterns

Register a singleton in a service provider

Don't authorize on every request; the auth token is valid for ~24h but the round trip is a wasted 200ms. Bind one client per process:

// app/Providers/AppServiceProvider.php
$this->app->singleton(\Esac\BackblazeB2\Client::class, function () {
    $client = new \Esac\BackblazeB2\Client();
    $client->authorizeAccount(
        config('services.b2.key_id'),
        config('services.b2.key')
    );
    return $client;
});

Add config/services.php:

'b2' => [
    'key_id'  => env('B2_APPLICATION_KEY_ID'),
    'key'     => env('B2_APPLICATION_KEY'),
    'bucket'  => env('B2_BUCKET_ID'),
],

Wrap it in a domain service, not controllers

The SDK is a transport layer. Give your app a B2Storage class with methods like store(UploadedFile $file): StoredFile and delete(string $fileId): void. This is your seam for swapping to the Flysystem B2 adapter later — and you probably will.

Upload pattern: stream, don't buffer

file_get_contents() on a 500MB video is a memory bomb. For small files (<100MB) it's fine. For anything larger, use the large-file API:

$large = $client->startLargeFile($bucketId, $fileName, 'video/mp4');
$part  = $client->getUploadPartUrl($large['fileId']);
$client->uploadPart($part['uploadUrl'], $part['authorizationToken'], 1, $chunk);
$client->finishLargeFile($large['fileId'], $sha1OfWholeFile, $partCount);

Note the SHA1 requirement on finishLargeFile — back it up or the file is orphaned.

Store metadata, not URLs

B2 download URLs are signed and expire. Persist fileId and fileName; generate the download URL on read:

$url = $client->getDownloadUrl($bucketName, $fileId); // or the auth-token variant

Backup/sync job pattern

listFileNames is paginated (startFileName, maxFileCount) and returns a nextFileName. Loop until it's null:

$cursor = null;
do {
    $page = $client->listFileNames($bucketId, $cursor, 1000);
    foreach ($page['files'] as $f) { /* upsert into DB */ }
    $cursor = $page['nextFileName'] ?? null;
} while ($cursor);

Queue long uploads

B2 uploads are slow and the SDK is synchronous. Push to a queued job, return a pending status, and let the job write fileId back to the model.

Testing

There's no mock client. Two options: (a) wrap the SDK behind your own interface and fake that, or (b) put a fake Client in the container via $this->mock(Client::class) and shouldReceive() the methods you use. Option (a) survives the imminent swap to a maintained library; option (b) is faster.

Gotchas and Tips

This package is archived and last released 2019-04-16. That's a real, not theoretical, problem. Consequences:

  • No PHP 8.1+ compatibility guarantee — expect deprecation notices from missing return types, and possible hard failures on strlen(null)-style bugs.
  • No support for newer B2 API features (e.g. bucket lifecycle rules, some file-info fields).
  • Zero dependents means nobody is filing issues. You are the maintainer now.
  • Before committing, run composer require on your PHP version and do a smoke test upload. If it breaks, vendor the source into app/Support/Backblaze or use the Flysystem B2 adapter instead.

License: LGPL-3.0. Fine for linking from a Laravel app, but if you modify the SDK source and distribute your app (SaaS is generally fine; shipped binaries are murkier), you may need to publish those modifications. Keeping it as an unmodified vendor dependency is the safe posture. Legal should glance at this if the product is commercial and distributed.

Bucket ID vs. bucket name. Most methods want bucketId; getDownloadUrl-style calls want bucketName. Mixing them yields 400 bad_request with a useless message. Keep both in config and be deliberate about which you pass where.

Auth tokens expire in 24 hours. In a long-running queue worker, a singleton authorized at boot will 401 after a day. Either authorize per job, or catch 401s and re-authorize once before retrying. A naive retry() in Horizon will not save you — every retry uses the same stale token.

Upload URLs are single-use and per-shard. getUploadUrl returns a URL valid for one upload. Do not cache it in Redis and reuse across requests; B2 will reject the second call. Fetch one per file.

Errors are inconsistent. The SDK may throw exceptions, return arrays with statusCode, or return false depending on the method and the fork's error handling. Read src/ for the method you're calling and code defensively. Log the full decoded response body — B2's code/message fields are the only thing that makes debugging possible.

Large-file SHA1. finishLargeFile requires a base64 SHA1 of the complete file. If you don't have it, the upload fails after you've paid for all the transfer. Compute it as you stream the chunks.

Memory limits on download. downloadFileById returns the body as a string. Set memory_limit deliberately or stream to a temp file; a 2GB backup restore will OOM a default PHP process.

Extension point: re-authorize. The most common patch developers make is a withRetry() decorator that catches 401/403, calls authorizeAccount again, and retries once. Do this before you hit production, not after.

Escape hatch. When (not if) this package fails on a PHP upgrade, the migration target is backblaze-b2 + Flysystem (or league/flysystem-b2), which gives you a Storage::disk('b2') Laravel-native API. Structuring your B2Storage service around file IDs now means that swap is a one-file change later.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator