mohamedsamy902/advanced-file-upload
Composer 安装命令:
composer require mohamedsamy902/advanced-file-upload
包简介
A production-ready Laravel package for chunked uploads, image processing, URL downloads, multi-cloud storage, SSRF protection, and resumable file transfers.
README 文档
README
A production-ready Laravel package for chunked uploads, image processing, URL downloads, multi-cloud storage, and resumable file transfers.
Contents
- Requirements
- Installation
- Configuration
- Usage
- Media Manager Dashboard (SPA)
- Security
- Events
- Testing
- Changelog
- Contributing
- License
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.1 |
| Laravel | >=10.0 |
| Intervention Image | ^3.0 |
| GD or Imagick | Required for image processing |
Installation
Install the package via Composer:
composer require mohamedsamy902/advanced-file-upload
Publish the configuration file:
php artisan vendor:publish --provider="MohamedSamy902\AdvancedFileUpload\AdvancedFileUploadServiceProvider" --tag=config
Publish and run the database migrations (required for file tracking or resumable uploads):
php artisan vendor:publish --provider="MohamedSamy902\AdvancedFileUpload\AdvancedFileUploadServiceProvider" --tag=migrations
php artisan migrate
For cloud storage, install the appropriate adapter:
# Amazon S3 composer require league/flysystem-aws-s3-v3 # Google Cloud Storage composer require spatie/laravel-google-cloud-storage
Configuration
After publishing, the configuration file is located at config/file-upload.php.
The key sections are:
return [ 'storage' => [ 'disk' => env('FILE_UPLOAD_DISK', 'public'), 'path' => env('FILE_UPLOAD_PATH', 'uploads'), 'default_folder' => null, 'cdn' => [ 'enabled' => env('FILE_UPLOAD_CDN_ENABLED', false), 'url' => env('FILE_UPLOAD_CDN_URL', ''), ], ], 'url_upload' => [ 'allowed_domains' => [], // empty = allow all public domains 'timeout_seconds' => 10, 'max_size_bytes' => 52428800, // 50 MB ], 'image_driver' => env('FILE_UPLOAD_IMAGE_DRIVER', 'gd'), // 'gd' or 'imagick' 'processing' => [ 'image' => [ 'enabled' => false, 'convert_to' => null, // 'webp', 'jpg', 'png' 'quality' => 85, 'max_width' => 1920, 'max_height' => null, 'upsize' => false, ], ], 'thumbnails' => [ 'enabled' => false, 'sizes' => [ 'sm' => ['width' => 150, 'height' => 150, 'crop' => true], 'md' => ['width' => 400, 'height' => 400, 'crop' => false], ], ], 'quota' => [ 'enabled' => false, 'per_user' => 1073741824, // 1 GB ], 'chunked' => [ 'session_ttl_hours' => 24, ], 'database' => [ 'enabled' => false, 'model' => \MohamedSamy902\AdvancedFileUpload\Models\FileUpload::class, ], ];
Usage
Single File Upload
use MohamedSamy902\AdvancedFileUpload\Facades\FileUpload; public function store(Request $request) { $result = FileUpload::upload($request->file('avatar')); // $result is a typed value object return response()->json([ 'path' => $result->path, 'url' => $result->url, 'type' => $result->type, // image | video | audio | pdf | document | other ]); }
The return value is an UploadResult object with these properties:
| Property | Type | Description |
|---|---|---|
status |
bool |
Whether the upload succeeded |
path |
string |
Storage path relative to the disk root |
url |
string |
Public URL (with CDN rewriting if enabled) |
originalName |
string |
The original client filename |
mimeType |
string |
The detected MIME type |
type |
string |
Logical category: image, video, audio, pdf, document, other |
size |
`int | null` |
thumbnailUrls |
array |
Map of thumbnail size name to URL |
databaseId |
`int | null` |
UploadResult also implements ArrayAccess, so existing code using array syntax continues to work:
$result = FileUpload::upload($file); $path = $result['path']; // still works $url = $result['url']; // still works
Batch Upload
Pass an array of files. Failed items return an error array without interrupting the batch.
$files = $request->file('documents'); $results = FileUpload::upload($files, [ 'folder_name' => 'reports', 'validation_rules' => ['file' => 'required|file|mimes:pdf,docx|max:10240'], ]); foreach ($results as $result) { if ($result instanceof \MohamedSamy902\AdvancedFileUpload\ValueObjects\UploadResult) { // success echo $result->url; } else { // failure — $result is an array with 'status', 'error', 'original_name' echo $result['error']; } }
URL Download and Upload
Download a remote file and store it, with SSRF protection applied before any HTTP request.
$result = FileUpload::upload([], [ 'url' => 'https://cdn.example.com/photo.jpg', 'folder_name' => 'avatars', ]);
Multiple URLs in a single call:
$results = FileUpload::upload([], [ 'url' => [ 'https://cdn.example.com/image1.jpg', 'https://cdn.example.com/image2.jpg', 'https://cdn.example.com/image3.jpg', ], 'folder_name' => 'gallery', ]);
Restricting permitted domains:
// config/file-upload.php 'url_upload' => [ 'allowed_domains' => ['cdn.myapp.com', 'assets.partner.com'], ],
Resumable Chunked Upload
When a client uploads a large file and the connection is interrupted, the upload can resume from the last successful chunk without starting over.
Step 1 — Start a session on your backend:
use MohamedSamy902\AdvancedFileUpload\Services\ResumableUploadService; public function startUpload(Request $request, ResumableUploadService $service) { $session = $service->startSession( originalName: $request->input('filename'), mimeType: $request->input('mime_type'), totalSize: (int) $request->input('total_size'), totalChunks: (int) $request->input('total_chunks'), folder: 'uploads', ); return response()->json(['session_id' => $session->session_id]); }
Step 2 — Send each chunk:
public function uploadChunk(Request $request, ResumableUploadService $service) { $state = $service->uploadChunk( sessionId: $request->input('session_id'), chunkIndex: (int) $request->input('chunk_index'), chunk: $request->file('chunk'), ); return response()->json($state); // {"received": 3, "total": 10, "missing": [3, 4, 5, 6, 7, 8, 9]} }
Step 3 — Resume after a failure:
The client queries which chunks are still missing:
public function sessionStatus(string $sessionId, ResumableUploadService $service) { return response()->json($service->getSession($sessionId)); // {"session_id": "...", "status": "pending", "received": 3, "total": 10, "missing": [3, 4, ...]} }
The client re-sends only the missing chunks, then calls complete.
Step 4 — Assemble and store:
public function completeUpload(Request $request, ResumableUploadService $service) { $result = $service->completeSession($request->input('session_id')); return response()->json([ 'path' => $result->path, 'url' => $result->url, ]); }
Image Processing
Enable image processing in the configuration:
'processing' => [ 'image' => [ 'enabled' => true, 'convert_to' => 'webp', // convert all uploads to WebP 'quality' => 85, 'max_width' => 1920, 'upsize' => false, // never enlarge images smaller than max_width ], ],
Override per request:
$result = FileUpload::upload($file, [ 'convert_to' => 'jpg', 'quality' => 75, ]);
Watermark:
// config/file-upload.php 'processing' => [ 'image' => [ 'enabled' => true, 'watermark' => [ 'enabled' => true, 'path' => storage_path('app/watermark.png'), 'position' => 'bottom-right', 'opacity' => 50, ], ], ],
Thumbnails:
'thumbnails' => [ 'enabled' => true, 'sizes' => [ 'sm' => ['width' => 150, 'height' => 150, 'crop' => true], 'md' => ['width' => 400, 'height' => 400, 'crop' => false], 'lg' => ['width' => 800, 'height' => null, 'crop' => false], ], ],
The thumbnail URLs are available in the result:
$result = FileUpload::upload($imageFile); echo $result->thumbnailUrls['sm']; // https://cdn.myapp.com/uploads/thumbs/sm_uuid.webp echo $result->thumbnailUrls['md'];
CDN URL Rewriting
When a CDN is configured, all generated URLs are automatically rewritten:
// config/file-upload.php 'storage' => [ 'cdn' => [ 'enabled' => true, 'url' => 'https://cdn.myapp.com', ], ],
$result = FileUpload::upload($file); echo $result->url; // https://cdn.myapp.com/uploads/uuid.jpg
Deleting Files
By storage path (when database tracking is disabled):
FileUpload::delete('uploads/uuid.jpg');
By database record ID (when tracking is enabled):
FileUpload::delete(42);
Delete multiple files. Partial failures do not abort the batch:
$results = FileUpload::delete([ 'uploads/file1.jpg', 'uploads/file2.pdf', 'uploads/file3.mp4', ]); foreach ($results as $result) { if (!$result['status']) { logger()->error($result['error']); } }
HasUploads Trait
You can attach the HasUploads trait to any Eloquent model to automatically retrieve its linked media.
use Illuminate\Database\Eloquent\Model; use MohamedSamy902\AdvancedFileUpload\Traits\HasUploads; class Article extends Model { use HasUploads; }
Then, quickly retrieve the image URL directly from your model:
$article = Article::find(1); // Get original image URL echo $article->getMediaUrl('image', 'original'); // Get thumbnail size (must be enabled in config) echo $article->getMediaUrl('image', 'small'); // Get an array of all thumbnails for the model's latest image $thumbs = $article->getThumbnails();
Media Manager Dashboard (SPA)
The package ships with a Production-Ready, Enterprise-grade Single Page Application (SPA) dashboard for tracking uploads, checking disk space, identifying unused orphaned files, and configuring settings safely.
Once installed and the config database.enabled is true, simply navigate to:
your-app.com/advanced-file-upload
Features:
- ⚡ PJAX Powered: Blazing fast, zero-reload navigation. Search inputs and filters preserve your cursor focus instantly.
- 🎨 Premium UI/UX: Built with modern Glassmorphism, smooth animations, and high-quality UI components.
- 📊 Filter-Aware Stats: The summary dashboard instantly updates metrics (Storage Used, Active/Unused Files) to match your search and filter parameters dynamically.
- 🧹 Bulk Actions & Orphan Scanning: Select multiple files to force-delete them from disk, or scan for missing database records easily.
Security
SSRF Protection
All URL downloads are validated before any HTTP request is issued. The following are blocked automatically:
Private IP address ranges (RFC 1918 and related):
| Range | Description |
|---|---|
127.0.0.0/8 |
IPv4 loopback |
10.0.0.0/8 |
Private network |
172.16.0.0/12 |
Private network |
192.168.0.0/16 |
Private network |
169.254.0.0/16 |
Link-local (AWS Metadata endpoint) |
100.64.0.0/10 |
Shared address space (RFC 6598) |
::1/128 |
IPv6 loopback |
fc00::/7 |
IPv6 unique local |
fe80::/10 |
IPv6 link-local |
Disallowed URL schemes:
Only http and https are permitted. The following are blocked: file://, ftp://, gopher://, data:, and all others.
Domain allowlist:
To restrict URL downloads to specific domains:
// config/file-upload.php 'url_upload' => [ 'allowed_domains' => ['cdn.myapp.com', 'assets.partner.com'], ],
An empty array permits any public domain.
Replacing Security Components
Every service is bound to a contract interface. To replace the SSRF validator with your own implementation:
// In AppServiceProvider::register() use MohamedSamy902\AdvancedFileUpload\Contracts\SsrfValidatorContract; $this->app->bind(SsrfValidatorContract::class, MyCustomSsrfValidator::class);
The same applies to ImageProcessorContract, QuotaManagerContract, and FileUploadContract.
Testing
Run the full test suite:
composer test
Run static analysis:
composer analyse
The package ships with 140 tests covering:
- Security attack simulation (SSRF, disguised files, DoS)
- Performance benchmarks (single file, batch of 50, memory usage)
- Error handling and edge cases
- Image processing with Intervention Image v3
- Resumable upload session management
- All public API surface
Changelog
v2.0.0
- Upgraded Intervention Image from v2 to v3
- Minimum PHP version raised to 8.1
- Minimum Laravel version raised to 10
- Moved cloud storage packages (
league/flysystem-aws-s3-v3,spatie/laravel-google-cloud-storage) tosuggest - Implemented comprehensive SSRF protection for URL downloads
- Added
UploadResulttyped value object (implementsArrayAccessfor backward compatibility) - Added resumable chunked upload system with session management
- Refactored monolithic service into focused, contract-based components
- Added 107 new tests (total: 140 tests, 345 assertions)
- Added GitHub Actions CI matrix for PHP 8.1–8.4 and Laravel 10–13
v1.0.0
- Initial release
Contributing
Contributions are welcome. Please open an issue first to discuss the change you intend to make.
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Write tests for your change
- Run the test suite:
composer test - Submit a pull request
License
The MIT License (MIT). Please see LICENSE for more information.
mohamedsamy902/advanced-file-upload 适用场景与选型建议
mohamedsamy902/advanced-file-upload 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 28 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 06 月 23 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「image」 「storage」 「s3」 「thumbnail」 「cdn」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 mohamedsamy902/advanced-file-upload 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 mohamedsamy902/advanced-file-upload 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 mohamedsamy902/advanced-file-upload 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A SDK for working with B2 cloud storage.
Small library to access Microsoft Windows Azure Blob Storage with a Service or a StreamWrapper.
Yii2 LightBox image galary widget uses Lightbox v2.10.0 by Lokesh Dhakar
The Yii2 extension uses jQuery jquery.carousel-1.1.min.js and makes image carousel from php array of structure defined.
A PHP library for fetching thumbnails from a URL
The Yii2 extension uses jQuery PrettyPhoto and OwlCarousel js and makes image galary from php array of structure defined.
统计信息
- 总下载量: 28
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 21
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-06-23