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

Dropbox Sdk Laravel Package

dropbox/dropbox-sdk

Official Dropbox PHP SDK for Dropbox API v2. Provides a generated client for files, sharing, users, auth, and team endpoints, with typed routes, exceptions, and Composer installation.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and create the app

composer require dropbox/dropbox-sdk

In the Dropbox App Console create a Scoped access app, pick a permission type (App folder vs Full Dropbox), and enable scopes up front — files.content.write, files.content.read, files.metadata.read, sharing.write, sharing.read. Missing scopes surface later as confusing 401/403s rather than clear errors.

Under the Permissions tab, generate an access token for local experimentation, and under Settings note the App key/secret. For anything beyond a throwaway script, use the refresh-token flow — Dropbox access tokens are short-lived (4 hours) and the SDK will not refresh them for you.

2. Wire credentials into Laravel

// config/services.php
'dropbox' => [
    'key'    => env('DROPBOX_APP_KEY'),
    'secret' => env('DROPBOX_APP_SECRET'),
    'token'  => env('DROPBOX_ACCESS_TOKEN'),     // short-lived
    'refresh_token' => env('DROPBOX_REFRESH_TOKEN'),
],

3. Bind the client once

// app/Providers/DropboxServiceProvider.php
public function register(): void
{
    $this->app->singleton(\Dropbox\Dropbox::class, function () {
        $app = new \Dropbox\DropboxApp(
            config('services.dropbox.key'),
            config('services.dropbox.secret'),
            Cache::remember('dropbox.token', 3 * 3600, fn () =>
                $this->refreshToken()),   // see Gotchas
        );
        return new \Dropbox\Dropbox($app);
    });
}

4. First use case — "export a report and give the user a link"

$dropbox = app(\Dropbox\Dropbox::class);

$dropbox->files()->upload(
    "/reports/{$order->id}.pdf",
    \Dropbox\Model\Files\WriteMode::add(),   // or ::overwrite()
    false,                                   // autorename
    false,                                   // mute
    null,
    Storage::get("reports/{$order->id}.pdf")
);

$link = $dropbox->sharing()->createSharedLinkWithSettings(
    "/reports/{$order->id}.pdf"
)->getUrl();

That covers roughly 80% of real usage: push a generated artefact, get a shareable URL back. Read vendor/dropbox/dropbox-sdk/src/Dropbox/ once — the generated model classes (Dropbox\Model\Files\FileMetadata, FolderMetadata, ListFolderResult) are the source of truth for what each response actually contains.


Implementation Patterns

Wrap the SDK — don't sprinkle it through controllers. The generated API is verbose and its positional argument lists are easy to get wrong. A thin DropboxStorage service with your domain methods (storeInvoice, mirrorFolder, temporaryUrlFor) keeps the SDK at one seam and makes the whole thing swappable/fakeable in tests.

final class DropboxStorage
{
    public function __construct(private \Dropbox\Dropbox $dropbox) {}

    public function temporaryUrl(string $path): string
    {
        // 4-hour link, no permission change on the user's account
        return $this->dropbox->files()->getTemporaryLink($path)->getLink();
    }
}

Choose the right abstraction level. For plain put/get/delete/list, use a Flysystem adapter behind Laravel's Storage facade (Storage::disk('dropbox')->put(...)) — it composes with Storage::fake() in tests and gives you url() semantics for free. Drop the raw SDK in only for what Flysystem can't do: shared links, getTemporaryLink, cursor-based deltas, file revisions, upload sessions, team/member scoping.

Cursor-based sync is the killer feature. For mirroring a Dropbox folder into your DB or S3, never poll listFolder recursively. Do an initial listFolder('', ['recursive' => true]), persist $result->getCursor(), then run a scheduled command that calls listFolderContinue($cursor) and only processes the delta:

$cursor = Cache::get('dropbox.cursor');

do {
    $page   = $dropbox->files()->listFolderContinue($cursor);
    $cursor = $page->getCursor();

    foreach ($page->getEntries() as $entry) {
        $entry instanceof \Dropbox\Model\Files\DeletedMetadata
            ? $this->handleDelete($entry->getPathLower())
            : $this->handleUpsert($entry);
    }
} while ($page->hasMore());

Cache::forever('dropbox.cursor', $cursor);

Wrap this in a queued job (ShouldBeUnique) and schedule it every few minutes — Dropbox cursors are designed for exactly this and consume far less rate limit than repeated listings.

Push large transfers to the queue. Anything over a few MB belongs in a ShouldQueue job with $tries = 3 and backoff(). Files above ~150 MB must use chunked uploadSessionStart / uploadSessionAppendV2 / uploadSessionFinish — the plain upload() endpoint rejects them. Store the session ID and byte offset in the job payload so retries resume rather than restart.

Stream downloads, don't buffer. download() returns a response object whose getContent() you can pipe straight through:

return response()->streamDownload(function () use ($dropbox, $path) {
    echo $dropbox->files()->download($path)->getContent();
}, basename($path));

For internal-only retrieval, skip Dropbox links entirely and stream through a signed Laravel route — you keep authorization in your own middleware rather than relying on anyone-with-the-link semantics.

Cache temporary links. getTemporaryLink is cheap but not free; you'll hit rate limits building pages with 50 thumbnails. Cache per path with a TTL under 4 hours (Cache::remember("dbx:{$path}", now()->addHours(3), ...)).

Webhook ingestion. Point a Dropbox webhook at a route excluded from CSRF, verify with hash_equals(hash_hmac('sha256', $request->getContent(), config('services.dropbox.secret')), $request->header('X-Dropbox-Signature')), then immediately queue the cursor sync above. Dropbox's payload only contains user IDs — it's a "something changed" ping, not data.


Gotchas and Tips

Short-lived tokens will bite you. Since Dropbox moved to expiring tokens, a static DROPBOX_ACCESS_TOKEN in .env works in dev and then dies in production four hours later. Persist the refresh_token, exchange it via POST https://api.dropbox.com/oauth2/token (grant_type=refresh_token, client_id, client_secret), cache the result with a safety margin, and rebuild the Dropbox instance when the token rolls over. The SDK does not do this transparently — you own the lifecycle.

Errors are typed, not stringly. Catch Dropbox\Exception, then inspect $e->getStatusCode() and $e->getResponse() (a generated error model such as PathRootError or WriteError). path/not_found inside a LookupError is very different from a 429 — treat them separately. Don't catch (Exception $e) { return null; } around SDK calls; you'll silently lose half your sync.

Rate limiting (429) is normal at scale. Respect Retry-After and wrap bulk operations in retry(5, fn () => ..., fn ($e) => $e->getStatusCode() === 429, 500). Batching many small file ops into one job that retries the whole batch will just amplify the problem — retry per item.

Paths are case-insensitive but case-preserving. /Reports/Q1.PDF and /reports/q1.pdf are the same file. Never use Dropbox paths as database keys or cache keys verbatim; normalize with mb_strtolower() or use the returned getPathLower(). Also: upload paths need a leading slash (/x.txt), root listings use ''.

autorename defaults to true. Upload to an existing path and you'll silently get report (1).pdf instead of an error or an overwrite. Set it to false and use WriteMode::overwrite() for deterministic behaviour — otherwise your "latest version" logic quietly reads a stale file.

Use content_hash for dedupe. Every FileMetadata carries Sha256 of the content. Store it; it lets you skip re-uploading unchanged files during a mirror and detect changes on the Dropbox side without downloading.

Namespace instability across majors. All the Dropbox\Model\... classes are code-generated and have been reshuffled between major versions. Pin the version in composer.json, and if you see Class "Dropbox\Model\Files\..." not found after an update, you're almost certainly on a version whose generated tree differs — check vendor/dropbox/dropbox-sdk/src/Dropbox/Model/ before upgrading.

Guzzle conflicts. The SDK pins Guzzle; if you're on a recent Laravel that already pulls guzzlehttp/guzzle ^7 and guzzlehttp/psr7 ^2, run composer why-not dropbox/dropbox-sdk at the first sign of trouble. The SDK accepts a custom GuzzleHttp\Client as the fourth DropboxApp argument — use that to inject your existing middleware stack (logging, Retry handler, correlation IDs) rather than maintaining a second client config.

Team apps need a different shape. Business/team-scoped tokens require selecting a member before file calls will return anything other than an empty root. If you're building an org-wide integration, confirm the SDK version you install exposes the team client (Dropbox\DropboxTeam) and route per-member through ->asMember($memberId); otherwise fall back to per-user OAuth instead of fighting the token model.

Testing. The SDK is concrete and hard to mock cleanly. Put DropboxStorage behind an interface, bind a fake in tests, and reserve the real client for a single integration test gated on an env flag. Never let a test suite hit the live API — a few hundred listFolder calls on CI will get your app throttled for everyone.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin