PHPackages                             nandung/s3-manager - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. nandung/s3-manager

ActiveLibrary[File &amp; Storage](/categories/file-storage)

nandung/s3-manager
==================

Laravel package for managing multiple S3-compatible storage buckets with quota management, URL embedding, and synchronization

v1.0.0(6mo ago)04MITPHPPHP ^8.2

Since Dec 21Pushed 6mo agoCompare

[ Source](https://github.com/nandung-id/s3-manager)[ Packagist](https://packagist.org/packages/nandung/s3-manager)[ Docs](https://github.com/nandung-id/s3-manager)[ GitHub Sponsors](https://github.com/sponsors/nandung-id)[ RSS](/packages/nandung-s3-manager/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (5)Versions (2)Used By (0)

Laravel S3 Manager
==================

[](#laravel-s3-manager)

[![Latest Version on Packagist](https://camo.githubusercontent.com/fb8bba4e923b88b073997a71470daa7b575be194782aa766066a6d81bd455d52/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e616e64756e672d69642f73332d6d616e616765722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/nandung/s3-manager)[![Total Downloads](https://camo.githubusercontent.com/488e56c1db4194fce6b644b0fed9a51045616f2ac06dd704a31138bb053df0bb/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6e616e64756e672d69642f73332d6d616e616765722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/nandung/s3-manager)[![License](https://camo.githubusercontent.com/dd137fc8b1fc5c2083e84f12dde991448663eb7e893c504b6fc099e0f5ccd54e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6e616e64756e672d69642f73332d6d616e616765722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/nandung-id/s3-manager)

A powerful Laravel package for managing multiple S3-compatible storage buckets with quota management, URL embedding, file synchronization, and comprehensive file tracking.

Features
--------

[](#features)

- 🪣 **Multi-Bucket Support** - Manage multiple S3-compatible storage providers (AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, etc.)
- 📊 **Quota Management** - Set storage limits per bucket and globally with automatic enforcement
- 🔗 **URL Generation** - Generate public URLs, presigned URLs, and embed proxy URLs
- 🔄 **Synchronization** - Sync remote bucket contents with local database for fast queries
- 📁 **File Tracking** - Track all uploaded files with metadata in your database
- 🚀 **Fluent API** - Clean, chainable interface for all operations
- 🎭 **Facade Support** - Use the convenient `S3Manager` facade
- ⚡ **Queue Support** - Background sync jobs for large buckets

Requirements
------------

[](#requirements)

- PHP 8.2 or higher
- Laravel 10.x to 12.x
- AWS SDK for PHP 3.x

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require nandung/s3-manager
```

Publish the configuration file:

```
php artisan vendor:publish --tag=s3-manager-config
```

Run the migrations:

```
php artisan migrate
```

Or publish migrations first if you want to customize them:

```
php artisan vendor:publish --tag=s3-manager-migrations
php artisan migrate
```

Configuration
-------------

[](#configuration)

After publishing, configure your buckets in `config/s3-manager.php`:

```
return [
    // Global storage quota (in bytes), null for unlimited
    'global_quota' => env('S3_MANAGER_GLOBAL_QUOTA', null),

    // Embed proxy configuration
    'embed' => [
        'enabled' => true,
        'route_prefix' => 'e',
        'cache_ttl' => 3600,
    ],

    // Presigned URL configuration
    'presigned' => [
        'default_expiration' => 3600, // seconds
    ],

    // Sync configuration
    'sync' => [
        'queue' => 'default',
        'chunk_size' => 1000,
    ],

    // Bucket configurations
    'buckets' => [
        'default' => [
            'driver' => 's3',
            'key' => env('S3_DEFAULT_KEY'),
            'secret' => env('S3_DEFAULT_SECRET'),
            'region' => env('S3_DEFAULT_REGION', 'us-east-1'),
            'bucket' => env('S3_DEFAULT_BUCKET'),
            'endpoint' => env('S3_DEFAULT_ENDPOINT'),
            'public_base_url' => env('S3_DEFAULT_PUBLIC_URL'),
            'quota' => env('S3_DEFAULT_QUOTA', null), // bytes
            'options' => [
                'use_path_style_endpoint' => false,
            ],
        ],
    ],
];
```

### Environment Variables

[](#environment-variables)

Add these to your `.env` file:

```
# Global quota (optional)
S3_MANAGER_GLOBAL_QUOTA=10737418240  # 10GB in bytes

# Default bucket configuration
S3_DEFAULT_KEY=your-access-key
S3_DEFAULT_SECRET=your-secret-key
S3_DEFAULT_REGION=us-east-1
S3_DEFAULT_BUCKET=your-bucket-name
S3_DEFAULT_ENDPOINT=https://s3.amazonaws.com
S3_DEFAULT_PUBLIC_URL=https://your-bucket.s3.amazonaws.com
S3_DEFAULT_QUOTA=5368709120  # 5GB in bytes
```

Usage
-----

[](#usage)

### Basic Usage with Facade

[](#basic-usage-with-facade)

```
use Nandung\S3Manager\Facades\S3Manager;

// Upload a file
$fileRecord = S3Manager::upload('default', 'images/photo.jpg', $fileContents);

// Upload with options
$fileRecord = S3Manager::upload('default', 'documents/report.pdf', $contents, [
    'ContentType' => 'application/pdf',
    'Metadata' => ['author' => 'John Doe'],
]);

// Download a file
$stream = S3Manager::download('default', 'images/photo.jpg');
$contents = $stream->getContents();

// Delete a file
S3Manager::delete('default', 'images/photo.jpg');

// Check if file exists
if (S3Manager::exists('default', 'images/photo.jpg')) {
    // File exists
}

// List files
$files = S3Manager::list('default', 'images/', recursive: true);
```

### Fluent Bucket API

[](#fluent-bucket-api)

```
use Nandung\S3Manager\Facades\S3Manager;

// Get bucket instance for fluent operations
$bucket = S3Manager::bucket('default');

// All operations are now scoped to this bucket
$fileRecord = $bucket->upload('images/photo.jpg', $contents);
$stream = $bucket->download('images/photo.jpg');
$bucket->delete('images/photo.jpg');
$exists = $bucket->exists('images/photo.jpg');
$files = $bucket->list('images/');
```

### URL Generation

[](#url-generation)

```
use Nandung\S3Manager\Facades\S3Manager;

// Public URL (requires public_base_url configuration)
$publicUrl = S3Manager::publicUrl('default', 'images/photo.jpg');
// Result: https://your-bucket.s3.amazonaws.com/images/photo.jpg

// Presigned URL (temporary access)
$presignedUrl = S3Manager::presignedUrl('default', 'images/photo.jpg', 3600);
// Result: https://...?X-Amz-Signature=...

// Presigned URL for upload
$uploadUrl = S3Manager::presignedUrl('default', 'uploads/new-file.jpg', 3600, 'PUT');

// Embed URL (proxied through your application)
$embedUrl = S3Manager::embedUrl('default', 'images/photo.jpg');
// Result: /e/default/images/photo.jpg
```

### Quota Management

[](#quota-management)

```
use Nandung\S3Manager\Facades\S3Manager;

// Get bucket usage
$usage = S3Manager::getUsage('default');
echo "Used: " . $usage->used . " bytes";
echo "Limit: " . $usage->limit . " bytes";
echo "Files: " . $usage->fileCount;
echo "Percent: " . $usage->percentUsed . "%";
echo "Unlimited: " . ($usage->isUnlimited ? 'Yes' : 'No');

// Get global usage (across all buckets)
$globalUsage = S3Manager::getUsage();
```

Quota exceptions are thrown automatically when limits are exceeded:

```
use Nandung\S3Manager\Exceptions\QuotaExceededException;
use Nandung\S3Manager\Exceptions\GlobalQuotaExceededException;

try {
    S3Manager::upload('default', 'large-file.zip', $contents);
} catch (QuotaExceededException $e) {
    // Bucket quota exceeded
    echo "Bucket {$e->bucketId} quota exceeded";
    echo "Limit: {$e->limit}, Used: {$e->used}, Attempted: {$e->attempted}";
} catch (GlobalQuotaExceededException $e) {
    // Global quota exceeded
    echo "Global storage quota exceeded";
}
```

### Synchronization

[](#synchronization)

Sync remote bucket contents with your local database:

```
use Nandung\S3Manager\Facades\S3Manager;

// Sync bucket (updates local database with remote state)
$result = S3Manager::sync('default');

echo "Added: " . $result->added;
echo "Updated: " . $result->updated;
echo "Deleted: " . $result->deleted;
echo "Total Files: " . $result->totalFiles;
echo "Total Size: " . $result->totalSize;
echo "Duration: " . $result->duration . " seconds";

if ($result->hasErrors()) {
    foreach ($result->errors as $error) {
        echo "Error: " . $error;
    }
}
```

### Dependency Injection

[](#dependency-injection)

```
use Nandung\S3Manager\Contracts\S3ManagerInterface;

class FileService
{
    public function __construct(
        private S3ManagerInterface $s3Manager
    ) {}

    public function uploadUserAvatar(User $user, $contents): string
    {
        $path = "avatars/{$user->id}.jpg";
        $this->s3Manager->upload('default', $path, $contents);
        return $this->s3Manager->publicUrl('default', $path);
    }
}
```

Multiple Bucket Configuration
-----------------------------

[](#multiple-bucket-configuration)

### AWS S3

[](#aws-s3)

```
'buckets' => [
    'aws' => [
        'driver' => 's3',
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
        'bucket' => env('AWS_BUCKET'),
        'endpoint' => null, // Use default AWS endpoint
        'public_base_url' => env('AWS_URL'),
        'quota' => null,
        'options' => [],
    ],
],
```

### Cloudflare R2

[](#cloudflare-r2)

```
'buckets' => [
    'r2' => [
        'driver' => 'r2',
        'key' => env('R2_ACCESS_KEY_ID'),
        'secret' => env('R2_SECRET_ACCESS_KEY'),
        'region' => 'auto',
        'bucket' => env('R2_BUCKET'),
        'endpoint' => env('R2_ENDPOINT'), // https://.r2.cloudflarestorage.com
        'public_base_url' => env('R2_PUBLIC_URL'),
        'quota' => null,
        'options' => [
            'use_path_style_endpoint' => false,
        ],
    ],
],
```

### MinIO (Self-hosted)

[](#minio-self-hosted)

```
'buckets' => [
    'minio' => [
        'driver' => 'minio',
        'key' => env('MINIO_ACCESS_KEY'),
        'secret' => env('MINIO_SECRET_KEY'),
        'region' => 'us-east-1',
        'bucket' => env('MINIO_BUCKET'),
        'endpoint' => env('MINIO_ENDPOINT', 'http://localhost:9000'),
        'public_base_url' => env('MINIO_PUBLIC_URL'),
        'quota' => 1073741824, // 1GB
        'options' => [
            'use_path_style_endpoint' => true,
        ],
    ],
],
```

### DigitalOcean Spaces

[](#digitalocean-spaces)

```
'buckets' => [
    'spaces' => [
        'driver' => 'spaces',
        'key' => env('DO_SPACES_KEY'),
        'secret' => env('DO_SPACES_SECRET'),
        'region' => env('DO_SPACES_REGION', 'nyc3'),
        'bucket' => env('DO_SPACES_BUCKET'),
        'endpoint' => env('DO_SPACES_ENDPOINT'), // https://nyc3.digitaloceanspaces.com
        'public_base_url' => env('DO_SPACES_URL'),
        'quota' => null,
        'options' => [],
    ],
],
```

Embed Proxy
-----------

[](#embed-proxy)

The embed proxy allows you to serve files through your application, useful for:

- Hiding actual S3 URLs
- Adding authentication/authorization
- Caching headers control
- Analytics tracking

### Configuration

[](#configuration-1)

```
'embed' => [
    'enabled' => true,
    'route_prefix' => 'e',  // URL prefix
    'cache_ttl' => 3600,    // Cache duration in seconds
],
```

### Usage

[](#usage-1)

```
// Generate embed URL
$embedUrl = S3Manager::embedUrl('default', 'images/photo.jpg');
// Result: /e/default/images/photo.jpg

// Use in views

```

The embed route automatically handles:

- Content-Type headers
- Cache-Control headers
- ETag headers
- Last-Modified headers

Database Schema
---------------

[](#database-schema)

The package creates two tables:

### s3\_manager\_files

[](#s3_manager_files)

Tracks all files across buckets:

ColumnTypeDescriptionidbigintPrimary keybucket\_idstring(64)Bucket identifierpathstring(1024)File path in bucketsizebigintFile size in bytesmime\_typestring(128)MIME typeetagstring(64)S3 ETaglast\_modifiedtimestampLast modification timemetadatajsonCustom metadatacreated\_attimestampRecord creation timeupdated\_attimestampRecord update time### s3\_manager\_bucket\_usage

[](#s3_manager_bucket_usage)

Tracks usage statistics per bucket:

ColumnTypeDescriptionidbigintPrimary keybucket\_idstring(64)Bucket identifiertotal\_sizebigintTotal storage usedfile\_countbigintNumber of fileslast\_synced\_attimestampLast sync timecreated\_attimestampRecord creation timeupdated\_attimestampRecord update timeException Handling
------------------

[](#exception-handling)

The package provides specific exceptions for different error scenarios:

```
use Nandung\S3Manager\Exceptions\BucketNotFoundException;
use Nandung\S3Manager\Exceptions\FileNotFoundException;
use Nandung\S3Manager\Exceptions\QuotaExceededException;
use Nandung\S3Manager\Exceptions\GlobalQuotaExceededException;
use Nandung\S3Manager\Exceptions\PublicUrlNotConfiguredException;
use Nandung\S3Manager\Exceptions\SyncException;

try {
    S3Manager::upload('unknown-bucket', 'file.txt', 'content');
} catch (BucketNotFoundException $e) {
    // Bucket not configured
}

try {
    S3Manager::download('default', 'non-existent.txt');
} catch (FileNotFoundException $e) {
    // File doesn't exist
}

try {
    S3Manager::publicUrl('default', 'file.txt');
} catch (PublicUrlNotConfiguredException $e) {
    // public_base_url not set for bucket
}
```

API Reference
-------------

[](#api-reference)

### S3Manager Methods

[](#s3manager-methods)

MethodDescription`bucket(string $bucketId)`Get fluent bucket interface`upload(string $bucketId, string $path, mixed $contents, array $options = [])`Upload a file`download(string $bucketId, string $path)`Download a file`delete(string $bucketId, string $path)`Delete a file`exists(string $bucketId, string $path)`Check if file exists`list(string $bucketId, string $prefix = '', bool $recursive = false)`List files`publicUrl(string $bucketId, string $path)`Generate public URL`presignedUrl(string $bucketId, string $path, ?int $expiration = null, string $method = 'GET')`Generate presigned URL`embedUrl(string $bucketId, string $path)`Generate embed proxy URL`sync(string $bucketId)`Sync bucket with database`getUsage(?string $bucketId = null)`Get usage statistics### FileRecord Model

[](#filerecord-model)

```
$fileRecord = S3Manager::upload('default', 'file.txt', 'content');

$fileRecord->bucket_id;    // string
$fileRecord->path;         // string
$fileRecord->size;         // int (bytes)
$fileRecord->mime_type;    // string
$fileRecord->etag;         // string
$fileRecord->last_modified; // Carbon
$fileRecord->metadata;     // array
```

### UsageInfo Object

[](#usageinfo-object)

```
$usage = S3Manager::getUsage('default');

$usage->used;        // int (bytes)
$usage->limit;       // int|null (bytes)
$usage->fileCount;   // int
$usage->percentUsed; // float
$usage->isUnlimited; // bool
```

### SyncResult Object

[](#syncresult-object)

```
$result = S3Manager::sync('default');

$result->added;      // int
$result->updated;    // int
$result->deleted;    // int
$result->totalFiles; // int
$result->totalSize;  // int (bytes)
$result->duration;   // float (seconds)
$result->errors;     // array
$result->hasErrors(); // bool
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

If you discover a security vulnerability, please send an email to . All security vulnerabilities will be promptly addressed.

Credits
-------

[](#credits)

- [Nandung](https://github.com/nandung-id)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

33

—

LowBetter than 72% of packages

Maintenance67

Regular maintenance activity

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

195d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7443e703954a15b5f431e0619be45d8902e5bfb159637e51b541dc50b13d2829?d=identicon)[nandung-id](/maintainers/nandung-id)

---

Top Contributors

[![nandunkz](https://avatars.githubusercontent.com/u/29007330?v=4)](https://github.com/nandunkz "nandunkz (4 commits)")

---

Tags

laravels3awsstorageminiofile managementdigitalocean-spacescloudflare-r2quota-managementmulti-bucket

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/nandung-s3-manager/health.svg)

```
[![Health](https://phpackages.com/badges/nandung-s3-manager/health.svg)](https://phpackages.com/packages/nandung-s3-manager)
```

###  Alternatives

[league/flysystem-aws-s3-v3

AWS S3 filesystem adapter for Flysystem.

1.7k285.7M1.0k](/packages/league-flysystem-aws-s3-v3)[aws/aws-sdk-php-laravel

A simple Laravel 9/10/11/12/13 service provider for including the AWS SDK for PHP.

1.7k38.2M84](/packages/aws-aws-sdk-php-laravel)[publiux/laravelcdn

Content Delivery Network (CDN) Package for Laravel

157234.9k](/packages/publiux-laravelcdn)[mwguerra/filemanager

A full-featured file manager package for Laravel and Filament v5 with dual operating modes, drag-and-drop uploads, S3/MinIO support, and comprehensive security features.

7620.3k3](/packages/mwguerra-filemanager)[kolay/xlsx-stream

Streaming XLSX reader and writer for PHP and Laravel. Constant memory regardless of file size, direct S3 multipart streaming, optional born-indexed random access.

437.9k](/packages/kolay-xlsx-stream)[juhasev/laravelcdn

Content Delivery Network (CDN) Package for Laravel

1921.1k](/packages/juhasev-laravelcdn)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
