eduvl / laravel-filekit
Typed file upload services for Laravel (images, audio, video, generic files) with signed Urls
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/eduvl/laravel-filekit
Requires
- php: ^8.2 || ^8.3 || ^8.4
- ext-fileinfo: *
- guzzlehttp/guzzle: ^7.8
- illuminate/contracts: >=10.0 <13.0
- illuminate/filesystem: >=10.0 <13.0
- illuminate/http: >=10.0 <13.0
- illuminate/routing: >=10.0 <13.0
- illuminate/support: >=10.0 <13.0
Requires (Dev)
- orchestra/testbench: ^9.0
README
Typed upload services for Laravel: images, audio, video and generic files with safe paths, MIME whitelists, and signed URLs for private disks.
✨ Features
- ✅ Upload from
UploadedFile, local path, HTTP URL, data:base64, or raw bytes - ✅ Per-type MIME whitelists and size limits
- ✅ Signed routes for private disks (
filekit.show,filekit.download) - ✅ Clean result object:
UploadResult { path, url, disk, mime, size } - ✅ Extensible (add your own
VideoService, etc.) - ✅ Laravel auto-discovery and publishable config
📚 Contents
- Requirements
- Installation
- Publish Config
- Quick Start
- API
- Signed URLs
- Validation
- Extending
- Security Notes
- Testing (Testbench)
- Troubleshooting
- Versioning
- Contributing
- License
✅ Requirements
| Component | Version |
|---|---|
| PHP | ^8.2 / ^8.3 / ^8.4 |
| Laravel | 10.x or 11.x |
| Guzzle | ^7.8 (installed automatically) |
📦 Installation
Packagist (when published)
composer require eduvl/laravel-filekit
From GitHub (VCS)
Add to your app’s composer.json:
"repositories": [ { "type": "vcs", "url": "https://github.com/eduvl/laravel-filekit" } ]
Then:
composer require eduvl/laravel-filekit:*@dev
Local path (for development)
"repositories": [ { "type": "path", "url": "../laravel-filekit", "options": { "symlink": true } } ]
composer require eduvl/laravel-filekit:*@dev
Uses Laravel auto-discovery — no manual provider registration.
⚙️ Publish Config
php artisan vendor:publish --provider="EduVl\FileKit\FileKitServiceProvider" --tag=filekit-config
This creates config/filekit.php:
return [ 'disk' => env('FILEKIT_DISK', 'public'), 'base_dirs' => [ 'files' => 'uploads', 'images' => 'uploads/images', 'audio' => 'uploads/audio', ], 'signed_url_ttl' => env('FILEKIT_SIGNED_TTL', 60), // minutes 'max_size_bytes' => env('FILEKIT_MAX_SIZE', 50 * 1024 * 1024), 'allowed' => [ 'images' => [ 'image/jpeg','image/png','image/gif','image/bmp','image/webp', 'image/svg+xml','image/x-icon','image/tiff','image/heic','image/heif', ], 'audio' => [ 'audio/mpeg','audio/wav','audio/ogg','audio/webm','audio/aac', 'audio/flac','audio/midi','audio/amr', ], 'files' => [ 'application/pdf','application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/rtf','application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation', 'application/epub+zip','text/plain','application/json','text/csv', 'application/zip','application/x-7z-compressed','application/x-rar-compressed', 'application/x-tar','application/gzip','application/x-bzip2', 'image/jpeg','image/png','image/webp', 'video/mp4','video/webm','video/quicktime', ], ], ];
Using the local public disk? Run:
php artisan storage:link
🚀 Quick Start
use Illuminate\Http\Request; use FileKit; class ProfilePhotoController { public function store(Request $request) { $request->validate([ 'photo' => ['required','image','max:5120'], // 5MB ]); $res = FileKit::images()->upload( $request->file('photo'), directory: 'users/'.auth()->id() ); return response()->json([ 'path' => $res->path, // uploads/images/users/123/uuid.jpg 'url' => $res->url, // public or signed URL 'mime' => $res->mime, // image/jpeg 'size' => $res->size, // bytes ]); } }
🧰 API
Facade → manager → services:
FileKit::images(); // ImageService FileKit::audio(); // AudioService FileKit::video(); // VideoService FileKit::files(); // Generic FileService
Upload
/** @var \EduVl\FileKit\Contracts\UploadResult $res */ $res = FileKit::images()->upload( $input, // UploadedFile | string (see below) filename: null, // optional, e.g. "avatar.jpg" directory: 'users/42' // subfolder appended to base_dir );
Allowed $input types
UploadedFile(form upload)- Local path string (
/tmp/x.png) - HTTP URL (
https://…) — downloaded via Laravel HTTP Client - Data URL (
data:image/png;base64,…) - Raw bytes string
Result object
$res->path; // disk-relative path $res->url; // public URL or temporary signed route $res->disk; // disk name $res->mime; // MIME type $res->size; // bytes
Delete
FileKit::files()->remove($res->path); // true (also true if already missing)
🔏 Signed URLs
- Public disk → direct
Storage::url($path). - Private disk → temporary signed routes:
filekit.show— inline streamfilekit.download— attachment
TTL is filekit.signed_url_ttl (minutes).
Manual download URL:
use Illuminate\Support\Facades\URL; $url = URL::temporarySignedRoute( 'filekit.download', now()->addMinutes(60), ['path' => $res->path] );
✅ Validation
Still validate requests at the controller level:
$request->validate([ 'file' => ['required', 'mimetypes:image/jpeg,image/png', 'max:10240'] // 10MB ]);
The package also enforces its own MIME whitelist and size caps.
🧩 Extending
Example: add ExcelService in your app:
namespace App\Services; use EduVl\FileKit\Services\FileService; class ExcelService extends FileService {}
- Add Exel MIME list and base dir in
config/filekit.php. - Optionally expose a factory method in your own manager wrapper.
🔐 Security Notes
- MIME whitelists per type
- Filename sanitization (no dangerous chars)
- No
../or absolute paths allowed indirectory - Size limit (
max_size_bytes) - HTTP downloads via Laravel HTTP Client (with timeout)
Recommended: domain allow-list, block local/loopback IPs (SSRF), verifyContent-Length.
🧪 Testing (Testbench)
This package targets Laravel; use Orchestra Testbench for package tests.
Install (in the package repo):
composer require --dev "orchestra/testbench:^9.0"
tests/TestCase.php
<?php namespace EduVl\FileKit\Tests; use EduVl\FileKit\FileKitServiceProvider; use Orchestra\Testbench\TestCase as Base; abstract class TestCase extends Base { protected function getPackageProviders($app) { return [FileKitServiceProvider::class]; } protected function defineEnvironment($app) { $app['config']->set('filekit.disk', 'public'); } }
Route test example:
<?php namespace EduVl\FileKit\Tests; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\URL; class RoutesTest extends TestCase { public function test_signed_show_serves_file() { Storage::fake('public'); Storage::disk('public')->put('uploads/test.txt', 'hello'); $url = URL::temporarySignedRoute('filekit.show', now()->addMinutes(5), [ 'path' => 'uploads/test.txt', ]); $this->get($url)->assertOk()->assertSee('hello'); } }
Run:
vendor/bin/phpunit
🛠️ Troubleshooting
-
Windows &
|in Composer constraints
Oncmd.exeuse ranges instead of pipes:
"illuminate/support:>=10.0 <12.0". -
404 when accessing URL
Ensure the file exists on the configured disk and the path matches.
For public disk:php artisan storage:link. -
IDE highlights helpers in the package
Outside a Laravel app those helpers aren’t loaded — it’s fine.
Keeping Testbench inrequire-devrestores helper awareness for IDE/tests.
🔢 Versioning
0.x— development, API may change.1.x— stable; breaking changes only in2.0.
🤝 Contributing
PRs and issues are welcome!
git clonecomposer installvendor/bin/phpunit- Don’t commit
vendor/orcomposer.lock(libraries usually don’t ship lockfiles)
📄 License
MIT — see LICENSE.