PHPackages                             kaibatech/viettel-cloud-s3 - 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. kaibatech/viettel-cloud-s3

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

kaibatech/viettel-cloud-s3
==========================

Viettel Cloud Object Storage - Laravel Storage Driver for VIPCore/EMC ViPR S3-compatible endpoints

v1.0.1(11mo ago)155MITPHPPHP ^8.2CI passing

Since May 25Pushed 11mo ago1 watchersCompare

[ Source](https://github.com/kaibatech/viettel-cloud-s3)[ Packagist](https://packagist.org/packages/kaibatech/viettel-cloud-s3)[ Docs](https://github.com/kaibatech/viettel-cloud-s3)[ RSS](/packages/kaibatech-viettel-cloud-s3/feed)WikiDiscussions main Synced 1mo ago

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

Viettel Cloud Object Storage - Laravel Storage Driver
=====================================================

[](#viettel-cloud-object-storage---laravel-storage-driver)

[![Latest Version on Packagist](https://camo.githubusercontent.com/397b46a6f447e2eb682ab27315c63be640173b3dfe3b2ec014dd27ea165b1d2b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6b61696261746563682f7669657474656c2d636c6f75642d73332e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kaibatech/viettel-cloud-s3)[![Total Downloads](https://camo.githubusercontent.com/13d995d1e2b2b8a9d474d2433c2e53cffa2f0db9de61ee41fcddc0f484b8ad0e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6b61696261746563682f7669657474656c2d636c6f75642d73332e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kaibatech/viettel-cloud-s3)[![License](https://camo.githubusercontent.com/9eb4268dc86c3a8cb75abc0ca699e03510edce67ab8441d57f5cd5c6174dd764/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6b61696261746563682f7669657474656c2d636c6f75642d73332e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/kaibatech/viettel-cloud-s3)

A Laravel Storage driver for **Viettel Cloud Object Storage** and other **VIPCore/EMC ViPR** S3-compatible endpoints that have signature compatibility issues with the standard AWS SDK for PHP.

✨ Features
----------

[](#-features)

- ✅ **Full Laravel Storage integration** - Use familiar `Storage::disk()` methods
- ✅ **Upload, download, delete files** with proper error handling
- ✅ **File existence checks** and metadata retrieval
- ✅ **Public/private file visibility** support (with ACL headers)
- ✅ **MIME type detection** for uploaded files
- ✅ **URL generation** for public file access
- ✅ **Custom AWS v4 signature calculation** compatible with VIPCore/EMC ViPR
- ✅ **UNSIGNED-PAYLOAD support** required by some S3-compatible services
- ✅ **Laravel 10.x, 11.x &amp; 12.x support**

🚀 Installation
--------------

[](#-installation)

Install the package via Composer:

```
composer require kaibatech/viettel-cloud-s3
```

### Laravel Auto-Discovery

[](#laravel-auto-discovery)

The package uses Laravel's auto-discovery feature, so the service provider will be registered automatically.

For Laravel versions that don't support auto-discovery, add the service provider to your `config/app.php`:

```
'providers' => [
    // ...
    Kaibatech\ViettelCloudS3\ViettelCloudS3ServiceProvider::class,
],
```

### Publish Configuration (Optional)

[](#publish-configuration-optional)

If you want to customize the configuration, publish the config file:

```
php artisan vendor:publish --tag=viettel-cloud-s3-config
```

⚙️ Configuration
----------------

[](#️-configuration)

Add a new disk to your `config/filesystems.php`:

```
'disks' => [
    // ... other disks

    'viettel-s3' => [
        'driver' => 'viettel-s3',
        'key' => env('VIETTEL_S3_ACCESS_KEY_ID'),
        'secret' => env('VIETTEL_S3_SECRET_ACCESS_KEY'),
        'region' => env('VIETTEL_S3_REGION', 'us-east-1'),
        'bucket' => env('VIETTEL_S3_BUCKET'),
        'url' => env('VIETTEL_S3_URL'),
        'endpoint' => env('VIETTEL_S3_ENDPOINT'),
        'throw' => false,
    ],
],
```

### Environment Variables

[](#environment-variables)

Add these variables to your `.env` file:

```
# Viettel Cloud Object Storage Configuration
VIETTEL_S3_ACCESS_KEY_ID=your-access-key
VIETTEL_S3_SECRET_ACCESS_KEY=your-secret-key
VIETTEL_S3_REGION=us-east-1
VIETTEL_S3_BUCKET=your-bucket-name
VIETTEL_S3_ENDPOINT=https://vcos.cloudstorage.com.vn
VIETTEL_S3_URL=https://your-access-key.vcos.cloudstorage.com.vn/your-bucket-name
```

### Alternative: Use Existing AWS Environment Variables

[](#alternative-use-existing-aws-environment-variables)

If you're migrating from AWS S3, you can reuse your existing environment variables:

```
'viettel-s3' => [
    'driver' => 'viettel-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'),
    'url' => env('AWS_URL'),
    'endpoint' => env('AWS_ENDPOINT'),
    'throw' => false,
],
```

📖 Usage
-------

[](#-usage)

### Basic File Operations

[](#basic-file-operations)

```
use Illuminate\Support\Facades\Storage;

// Upload a file
$content = 'Hello, Viettel Cloud!';
$path = 'documents/hello.txt';

Storage::disk('viettel-s3')->put($path, $content, [
    'visibility' => 'public',
    'mimetype' => 'text/plain'
]);

// Check if file exists
if (Storage::disk('viettel-s3')->exists($path)) {
    echo "File exists!";
}

// Download file content
$content = Storage::disk('viettel-s3')->get($path);

// Get file size
$size = Storage::disk('viettel-s3')->size($path);

// Get file URL
$url = Storage::disk('viettel-s3')->url($path);

// Delete file
Storage::disk('viettel-s3')->delete($path);
```

### File Upload with Form Validation

[](#file-upload-with-form-validation)

```
public function uploadFile(Request $request)
{
    $request->validate([
        'file' => 'required|file|max:10240', // 10MB max
    ]);

    $file = $request->file('file');
    $filename = time() . '_' . $file->getClientOriginalName();

    // Upload using Viettel S3 driver
    $path = Storage::disk('viettel-s3')->putFileAs(
        'uploads',
        $file,
        $filename,
        ['visibility' => 'public']
    );

    return response()->json([
        'success' => true,
        'path' => $path,
        'url' => Storage::disk('viettel-s3')->url($path),
        'size' => $file->getSize(),
    ]);
}
```

### Batch Operations

[](#batch-operations)

```
// Upload multiple files
$files = [
    'file1.txt' => 'Content 1',
    'file2.txt' => 'Content 2',
    'file3.txt' => 'Content 3',
];

foreach ($files as $filename => $content) {
    Storage::disk('viettel-s3')->put("batch/{$filename}", $content, [
        'visibility' => 'public'
    ]);
}

// Delete multiple files
$filesToDelete = ['batch/file1.txt', 'batch/file2.txt', 'batch/file3.txt'];
Storage::disk('viettel-s3')->delete($filesToDelete);
```

### Working with Streams

[](#working-with-streams)

```
// Upload from stream
$stream = fopen('/path/to/large-file.zip', 'r');
Storage::disk('viettel-s3')->putStream('large-files/archive.zip', $stream);
fclose($stream);

// Read as stream
$stream = Storage::disk('viettel-s3')->readStream('large-files/archive.zip');
// Process stream...
```

### File Metadata

[](#file-metadata)

```
$path = 'documents/example.pdf';

// Get file information
$exists = Storage::disk('viettel-s3')->exists($path);
$size = Storage::disk('viettel-s3')->size($path);
$lastModified = Storage::disk('viettel-s3')->lastModified($path);
$mimeType = Storage::disk('viettel-s3')->mimeType($path);
$url = Storage::disk('viettel-s3')->url($path);

echo "File: {$path}\n";
echo "Exists: " . ($exists ? 'Yes' : 'No') . "\n";
echo "Size: {$size} bytes\n";
echo "Last Modified: " . date('Y-m-d H:i:s', $lastModified) . "\n";
echo "MIME Type: {$mimeType}\n";
echo "URL: {$url}\n";
```

🔧 Advanced Configuration
------------------------

[](#-advanced-configuration)

### Custom User Agent

[](#custom-user-agent)

The driver uses a default user agent `viettel-cloud-s3/1.0 callback`. This is configured in the adapter and matches the working signature requirements.

### File Visibility and ACL

[](#file-visibility-and-acl)

```
// Upload with public visibility (adds x-amz-acl: public-read header)
Storage::disk('viettel-s3')->put($path, $content, [
    'visibility' => 'public'
]);

// Upload as private (default)
Storage::disk('viettel-s3')->put($path, $content);
// or explicitly
Storage::disk('viettel-s3')->put($path, $content, [
    'visibility' => 'private'
]);
```

**⚠️ VIPCore Limitation**: While the driver correctly sends ACL headers, VIPCore/EMC ViPR may not support anonymous public access like AWS S3. Files may still require authentication regardless of the ACL setting.

### Error Handling

[](#error-handling)

```
try {
    Storage::disk('viettel-s3')->put($path, $content);
    echo "Upload successful!";
} catch (\League\Flysystem\UnableToWriteFile $e) {
    echo "Upload failed: " . $e->getMessage();
} catch (\Exception $e) {
    echo "General error: " . $e->getMessage();
}
```

🏗️ How It Works
---------------

[](#️-how-it-works)

### The Problem

[](#the-problem)

Standard AWS SDK for PHP calculates signatures differently than what VIPCore/EMC ViPR S3-compatible services expect, causing `SignatureDoesNotMatch` errors.

### The Solution

[](#the-solution)

This package provides a custom Flysystem adapter that:

1. **Manually calculates AWS v4 signatures** using the exact format expected by VIPCore
2. **Forces `UNSIGNED-PAYLOAD`** content hash (required by VIPCore)
3. **Uses direct cURL requests** bypassing AWS SDK signature issues
4. **Implements proper header formatting** based on working examples

### Key Components

[](#key-components)

- **Custom signature calculation** compatible with VIPCore/EMC ViPR
- **Proper canonical request formatting** with alphabetical header ordering
- **UNSIGNED-PAYLOAD handling** for all requests
- **cURL-based HTTP client** for direct control over requests

🔒 Security
----------

[](#-security)

- Uses AWS v4 signature validation
- Proper credential handling through Laravel configuration
- Request timestamp validation prevents replay attacks
- Content integrity checks with SHA256 hashing
- Supports both public and private file access controls

🧪 Testing
---------

[](#-testing)

After installation, you can test the integration with a simple script:

```
// Test basic functionality
$disk = Storage::disk('viettel-s3');

// Upload test
$testFile = 'test-' . time() . '.txt';
$testContent = 'Hello from Viettel Cloud S3!';

$disk->put($testFile, $testContent, ['visibility' => 'public']);

// Verify upload
if ($disk->exists($testFile)) {
    echo "✅ Upload successful\n";

    // Test download
    $downloadedContent = $disk->get($testFile);
    if ($downloadedContent === $testContent) {
        echo "✅ Download successful\n";
    }

    // Test URL generation
    $url = $disk->url($testFile);
    echo "📁 File URL: {$url}\n";

    // Cleanup
    $disk->delete($testFile);
    echo "🗑️ Cleanup completed\n";
}
```

🐛 Troubleshooting
-----------------

[](#-troubleshooting)

### Common Issues

[](#common-issues)

**1. SignatureDoesNotMatch Error**

- ✅ **Solved by this package!** The custom signature calculation handles VIPCore compatibility.

**2. File Upload Fails**

- Check your credentials in `.env`
- Verify bucket name and endpoint URL
- Ensure network connectivity to the endpoint

**3. File URLs Don't Work**

- Verify the `VIETTEL_S3_URL` environment variable
- Check if the bucket and file permissions are correct
- Remember that VIPCore may require authentication even for "public" files

**4. Large File Uploads**

- Use `putStream()` for large files instead of `put()`
- Consider implementing chunked uploads for files &gt; 100MB

### Debug Mode

[](#debug-mode)

Enable debug logging in your Laravel application to see detailed request/response information:

```
// In config/logging.php, set the default log level to 'debug'
'level' => 'debug',
```

Check `storage/logs/laravel.log` for detailed error information.

📋 Requirements
--------------

[](#-requirements)

- **PHP**: ^8.2
- **Laravel**: ^10.0 || ^11.0 || ^12.0
- **League/Flysystem**: ^3.0

🤝 Contributing
--------------

[](#-contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

📝 License
---------

[](#-license)

This package is open-sourced software licensed under the [MIT license](LICENSE).

🏢 Support
---------

[](#-support)

- **Issues**: [GitHub Issues](https://github.com/kaibatech/viettel-cloud-s3/issues)
- **Documentation**: This README and inline code documentation
- **Community**: Feel free to open discussions for questions and feature requests

🎯 Roadmap
---------

[](#-roadmap)

- Add support for multipart uploads
- Implement proper directory listing (ListObjects API)
- Add comprehensive test suite
- Support for more VIPCore-specific features
- Performance optimizations and caching

---

**🎉 Happy coding with Viettel Cloud Object Storage!**

###  Health Score

33

—

LowBetter than 74% of packages

Maintenance54

Moderate activity, may be stable

Popularity10

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity50

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

Every ~0 days

Total

2

Last Release

349d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5853fb85dffee9f845566532109342214d912205f1327585b6607b20d879b7c4?d=identicon)[kaibatech](/maintainers/kaibatech)

---

Top Contributors

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

---

Tags

laravellaravel-storageFlysystemlaravels3cloudstorageemcviettelvipcorevipr

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/kaibatech-viettel-cloud-s3/health.svg)

```
[![Health](https://phpackages.com/badges/kaibatech-viettel-cloud-s3/health.svg)](https://phpackages.com/packages/kaibatech-viettel-cloud-s3)
```

###  Alternatives

[league/flysystem-aws-s3-v3

AWS S3 filesystem adapter for Flysystem.

1.6k263.6M788](/packages/league-flysystem-aws-s3-v3)[unisharp/laravel-filemanager

A file upload/editor intended for use with Laravel 5 to 10 and CKEditor / TinyMCE

2.2k3.3M73](/packages/unisharp-laravel-filemanager)[zing/laravel-flysystem-obs

Flysystem Adapter for OBS

1211.2k](/packages/zing-laravel-flysystem-obs)[innoge/laravel-rclone

A sleek PHP wrapper around rclone with Laravel-style fluent API syntax

174.1k](/packages/innoge-laravel-rclone)[yoelpc4/laravel-cloudinary

Laravel Cloudinary filesystem cloud driver.

3343.0k](/packages/yoelpc4-laravel-cloudinary)

PHPackages © 2026

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