yangyifan/upload
A Laravel file upload package that simplifies single and multiple uploads with validation, storage integration, and customizable workflows. It works with Laravel’s filesystem disks, supports local and cloud storage, and helps handle file/image uploads cleanly in applications.
Install: composer require yangyifan/upload. Laravel's auto-discovery should register the service provider; on older Laravel versions add Yangyifan\Upload\UploadServiceProvider to config/app.php manually.
Publish the config (first place to look after install):
php artisan vendor:publish --provider="Yangyifan\Upload\UploadServiceProvider"
This drops config/upload.php — read it before writing any code. It contains the driver (local, qiniu, oss, cos), upload path, allowed extensions, max size, and route settings.
If the config registers a route, the package exposes an upload endpoint (default POST /upload, prefix configurable in config/upload.php). Your frontend posts a multipart/form-data file and receives JSON back with the stored path/URL.
For local storage, run php artisan storage:link so the returned URLs actually resolve.
First use case: upload a user avatar from a form, persist the returned path in users.avatar, and render it with Storage::disk(...)->url($path).
Smoke-test with curl against the route before wiring the frontend — it confirms routing, CSRF middleware, and disk credentials in one shot.
Route-based (fastest path). Let the package own the endpoint. Your frontend POSTs to /upload, receives JSON, and your controller stores the returned path. Minimal code, but you inherit the package's validation and response shape.
Trait-based (recommended for anything real). The package ships an upload trait intended to be used inside your own controller. use it, then override the disk/path/validation hooks so uploads land in a tenant- or user-scoped folder:
use Yangyifan\Upload\Traits\UploadTrait;
class AvatarController extends Controller
{
use UploadTrait;
protected $disk = 'public';
protected $path = 'avatars';
}
This keeps your auth middleware, policies, and DB writes in your own code while still reusing the SDK's file handling.
Driver swap per environment. Keep local in development and switch config/upload.php to qiniu/oss/cos in production. Application code should never reference the driver — store {disk, path} in the DB and resolve URLs later, so a CDN migration doesn't require a data backfill.
Wrap it behind your own service. Create App\Services\UploadService with an upload(UploadedFile $file): string method that delegates to the package. When the package goes unmaintained (see gotchas), you replace one class instead of hunting call sites.
Validation before upload. Add a FormRequest with mimes: and max: rules in front of your own endpoint. Treat the package's built-in checks as a second line of defense, not the only one.
Response contract. Cloud drivers commonly return the full URL while local returns a relative path. Normalize in your controller before returning to the client, and never persist the URL — persist the path.
Post-upload lifecycle. Store the disk and path, generate signed/temporary URLs at render time, and delete via Storage::disk($disk)->delete($path) when the record is removed. Run a scheduled command to sweep orphans.
config/upload.php (or disable the auto route and mount your own), anyone can push files to your disk. Check php artisan route:list | grep upload first.yangyifan/upload) and repo (tyua07/laravel-upload) differ, which makes searching confusing. The config file and the package's controller/trait source are the real documentation.composer.json and test on the Laravel version you'll actually deploy. Budget for a fork.max_size in the config is bytes or kilobytes. A wrong assumption shows up as "uploads silently rejected" rather than an error you can trace.../.storage:link on every deploy. Local-disk URLs 404 after a fresh deploy until the symlink is recreated. Put it in your deploy script.upload_max_filesize / post_max_size in php.ini or client_max_body_size in nginx, not from the SDK. Check $_FILES['file']['error'] and storage/logs/laravel.log.How can I help you explore Laravel packages today?