# Livewire Filemanager ## Introduction Livewire Filemanager is a comprehensive file management package designed specifically for Laravel applications. Built with Livewire 3 and powered by Spatie's Media Library, it provides a modern, interactive interface for managing files and folders within Laravel projects. The package offers intuitive drag-and-drop functionality, real-time search capabilities, and seamless integration with Laravel's ecosystem, making it an ideal solution for developers who need robust file management without the complexity of building it from scratch. The package combines a rich user interface with a complete RESTful API, supporting both interactive web-based file management and programmatic access via API endpoints. It includes built-in support for multi-language interfaces (11 languages), dark mode theming, automatic thumbnail generation for images, and optional access control lists (ACL) for multi-user environments. The architecture leverages Laravel's authentication system, queue management for background processing, and follows Laravel's conventions for easy integration and maintenance. ## Installation and Basic Setup ### Install Package via Composer ```bash composer require livewire-filemanager/filemanager ``` ### Publish and Run Migrations ```bash # Publish filemanager migrations php artisan vendor:publish --tag=livewire-fileuploader-migrations # Publish Spatie Media Library migrations (if not already done) php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-migrations" # Run migrations to create folders and media tables php artisan migrate ``` ### Basic Implementation in Blade Template ```html File Manager @filemanagerStyles

My File Manager

@filemanagerScripts ``` ### Production Setup with Tailwind CSS ```javascript // tailwind.config.js module.exports = { content: [ './resources/**/*.blade.php', './vendor/livewire-filemanager/filemanager/resources/views/**/*.blade.php', ], theme: { extend: {}, }, plugins: [], } ``` ```bash # Rebuild Tailwind npm run build # Start queue workers for thumbnail generation php artisan queue:work ``` ## Configuration ### Publish Configuration File ```bash php artisan vendor:publish --tag=livewire-fileuploader-config ``` ### Complete Configuration Example ```php true, // API Configuration 'api' => [ 'enabled' => true, 'prefix' => 'filemanager/v1', 'middleware' => ['api', 'auth:sanctum'], 'rate_limit' => '100,1', 'max_file_size' => 10240, // KB 'allowed_extensions' => [ 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'txt', 'zip' ], 'chunk_size' => 1048576, // 1MB ], // Folder Configuration 'folders' => [ 'max_depth' => 5, // Maximum folder nesting level ], // Custom Callbacks 'callbacks' => [ 'before_upload' => function($file) { // Validate file before upload \Log::info('Uploading file: ' . $file->getClientOriginalName()); }, 'after_upload' => function($media) { // Process file after upload \Log::info('File uploaded: ' . $media->file_name); }, 'access_check' => function($user, $media) { // Custom access control logic return $user->hasRole('admin') || $media->custom_properties['user_id'] === $user->id; }, ], ]; ``` ## API Authentication Setup ### Configure Laravel Sanctum for API Access ```bash # Install Sanctum (if not already installed) composer require laravel/sanctum # Publish Sanctum configuration php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider" # Run Sanctum migrations php artisan migrate ``` ### Generate API Token for User ```php createToken('filemanager-api')->plainTextToken; // Output: 1|abcdef123456... echo $token; ``` ### Test API Authentication ```bash # Store token in variable TOKEN="1|abcdef123456..." # Test authenticated request curl -X GET "https://your-domain.com/api/filemanager/v1/folders" \ -H "Accept: application/json" \ -H "Authorization: Bearer $TOKEN" ``` ## API - List Folders ### Get All Folders with Filtering ```bash # List all folders curl -X GET "https://your-domain.com/api/filemanager/v1/folders" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" # Filter by parent folder curl -X GET "https://your-domain.com/api/filemanager/v1/folders?parent_id=5" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" # Search folders by name curl -X GET "https://your-domain.com/api/filemanager/v1/folders?search=documents" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` ```json { "data": [ { "id": 1, "name": "Documents", "slug": "documents", "parent_id": null, "user_id": 1, "created_at": "2024-01-15T10:30:00.000000Z", "updated_at": "2024-01-15T10:30:00.000000Z", "children": [], "media": [] }, { "id": 2, "name": "Images", "slug": "images", "parent_id": null, "user_id": 1, "created_at": "2024-01-15T11:00:00.000000Z", "updated_at": "2024-01-15T11:00:00.000000Z", "children": [], "media": [] } ] } ``` ## API - Create Folder ### Create New Folder in Hierarchy ```bash # Create root folder curl -X POST "https://your-domain.com/api/filemanager/v1/folders" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -d '{ "name": "Projects", "parent_id": null }' # Create nested folder curl -X POST "https://your-domain.com/api/filemanager/v1/folders" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -d '{ "name": "2024 Projects", "parent_id": 1 }' ``` ```json { "data": { "id": 3, "name": "Projects", "slug": "projects", "parent_id": null, "user_id": 1, "created_at": "2024-01-15T14:22:00.000000Z", "updated_at": "2024-01-15T14:22:00.000000Z", "children": [], "media": [] } } ``` ## API - Update Folder ### Rename Existing Folder ```bash # Update folder name curl -X PUT "https://your-domain.com/api/filemanager/v1/folders/3" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -d '{ "name": "Active Projects" }' ``` ```json { "data": { "id": 3, "name": "Active Projects", "slug": "active-projects", "parent_id": null, "user_id": 1, "created_at": "2024-01-15T14:22:00.000000Z", "updated_at": "2024-01-15T14:30:00.000000Z", "children": [], "media": [] } } ``` ## API - Delete Folder ### Remove Folder and Contents ```bash # Delete folder (cannot delete home/root folders) curl -X DELETE "https://your-domain.com/api/filemanager/v1/folders/3" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` ```json { "message": "Folder deleted successfully" } ``` ```bash # Attempt to delete home folder returns error curl -X DELETE "https://your-domain.com/api/filemanager/v1/folders/1" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` ```json { "message": "Cannot delete home folder" } ``` ## API - Get Folder Details ### Retrieve Single Folder with Relationships ```bash # Get folder with children and media curl -X GET "https://your-domain.com/api/filemanager/v1/folders/1" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` ```json { "data": { "id": 1, "name": "Documents", "slug": "documents", "parent_id": null, "user_id": 1, "created_at": "2024-01-15T10:30:00.000000Z", "updated_at": "2024-01-15T10:30:00.000000Z", "children": [ { "id": 5, "name": "Contracts", "slug": "contracts", "parent_id": 1, "created_at": "2024-01-15T11:00:00.000000Z" } ], "media": [ { "id": 10, "name": "invoice.pdf", "file_name": "invoice.pdf", "mime_type": "application/pdf", "size": 245760 } ] } } ``` ## API - Upload File to Folder ### Upload Single or Multiple Files ```bash # Upload single file to specific folder curl -X POST "https://your-domain.com/api/filemanager/v1/folders/1/upload" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -F "files[]=@/path/to/document.pdf" \ -F "files[]=@/path/to/image.jpg" ``` ```json { "message": "Files uploaded successfully", "files": [ { "id": 15, "name": "document.pdf", "file_name": "document.pdf", "mime_type": "application/pdf", "size": 524288, "url": "https://your-domain.com/media/15/document.pdf", "thumbnail_url": null, "created_at": "2024-01-15T15:00:00.000000Z" }, { "id": 16, "name": "image.jpg", "file_name": "image.jpg", "mime_type": "image/jpeg", "size": 102400, "url": "https://your-domain.com/media/16/image.jpg", "thumbnail_url": "https://your-domain.com/media/16/conversions/image-thumbnail.jpg", "created_at": "2024-01-15T15:00:00.000000Z" } ] } ``` ## API - Bulk File Upload ### Upload Multiple Files with Error Handling ```bash # Bulk upload with mixed success/failure curl -X POST "https://your-domain.com/api/filemanager/v1/files/bulk" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -F "folder_id=1" \ -F "files[]=@/path/to/valid-file.pdf" \ -F "files[]=@/path/to/another-file.jpg" \ -F "files[]=@/path/to/invalid-file.exe" ``` ```json { "message": "Bulk upload completed", "uploaded": 2, "failed": 1, "files": [ { "id": 20, "name": "valid-file.pdf", "file_name": "valid-file.pdf", "mime_type": "application/pdf", "size": 1048576 }, { "id": 21, "name": "another-file.jpg", "file_name": "another-file.jpg", "mime_type": "image/jpeg", "size": 204800 } ], "errors": [ { "file": "invalid-file.exe", "error": "The file type is not allowed" } ] } ``` ## API - List Files ### Query Files with Pagination and Filtering ```bash # List all files with pagination curl -X GET "https://your-domain.com/api/filemanager/v1/files?page=1&per_page=20" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" # Filter files by folder curl -X GET "https://your-domain.com/api/filemanager/v1/files?folder_id=1" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" # Search files by name curl -X GET "https://your-domain.com/api/filemanager/v1/files?search=invoice" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` ```json { "data": [ { "id": 10, "name": "invoice-2024.pdf", "file_name": "invoice-2024.pdf", "mime_type": "application/pdf", "size": 245760, "url": "https://your-domain.com/media/10/invoice-2024.pdf", "thumbnail_url": null, "folder_id": 1, "created_at": "2024-01-10T09:30:00.000000Z", "updated_at": "2024-01-10T09:30:00.000000Z" }, { "id": 11, "name": "logo.png", "file_name": "logo.png", "mime_type": "image/png", "size": 51200, "url": "https://your-domain.com/media/11/logo.png", "thumbnail_url": "https://your-domain.com/media/11/conversions/logo-thumbnail.jpg", "folder_id": 2, "created_at": "2024-01-12T14:00:00.000000Z", "updated_at": "2024-01-12T14:00:00.000000Z" } ] } ``` ## API - Update File ### Rename File ```bash # Update file name curl -X PUT "https://your-domain.com/api/filemanager/v1/files/10" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -d '{ "name": "invoice-january-2024.pdf" }' ``` ```json { "data": { "id": 10, "name": "invoice-january-2024.pdf", "file_name": "invoice-january-2024.pdf", "mime_type": "application/pdf", "size": 245760, "url": "https://your-domain.com/media/10/invoice-january-2024.pdf", "created_at": "2024-01-10T09:30:00.000000Z", "updated_at": "2024-01-15T16:20:00.000000Z" } } ``` ## API - Delete File ### Remove File from Storage ```bash # Delete specific file curl -X DELETE "https://your-domain.com/api/filemanager/v1/files/10" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` ```json { "message": "File deleted successfully" } ``` ## API - Download File ### Retrieve File Content ```bash # Download file (returns file content or download prompt) curl -X GET "https://your-domain.com/api/filemanager/v1/files/10" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -o downloaded-file.pdf # For images/PDFs, displays inline in browser # For other types, triggers download ``` ## Livewire Component Usage ### Programmatic File Upload in Livewire ```php selectedFolder = Folder::firstOrCreate([ 'name' => 'Documents', 'parent_id' => null, 'user_id' => Auth::id() ]); } public function uploadDocument() { $this->validate([ 'document' => 'required|file|max:10240|mimes:pdf,doc,docx' ]); try { // Upload file to folder $media = $this->selectedFolder ->addMedia($this->document->getRealPath()) ->usingName($this->document->getClientOriginalName()) ->withCustomProperties([ 'user_id' => Auth::id(), 'uploaded_via' => 'livewire-component' ]) ->toMediaCollection('medialibrary'); session()->flash('success', 'Document uploaded successfully!'); return redirect()->route('documents.show', $media->id); } catch (\Exception $e) { session()->flash('error', 'Upload failed: ' . $e->getMessage()); } } public function render() { return view('livewire.document-uploader'); } } ``` ## Direct File Access Routes ### Enable Public File Access ```php where('path', '.*') ->name('assets.show') ->middleware(['auth']); // Optional: require authentication // Example URLs generated: // https://your-domain.com/files/documents/invoice.pdf // https://your-domain.com/files/images/logo.png ``` ### Generate File URL in Blade ```blade @php $file = \LivewireFilemanager\Filemanager\Models\Media::find(10); @endphp {{ $file->name }} View {{ $file->name }} Download File ``` ## Access Control (ACL) Implementation ### Enable User-Scoped File Access ```bash # Publish config files php artisan vendor:publish --tag=livewire-fileuploader-config php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-config" ``` ```php true, // ... other config ]; ``` ```php \LivewireFilemanager\Filemanager\Models\Media::class, // ... other config ]; ``` ```php [ 'before_upload' => function($file) { // Validate file type $allowedMimes = ['image/jpeg', 'image/png', 'application/pdf']; if (!in_array($file->getMimeType(), $allowedMimes)) { throw new \Exception('File type not allowed'); } // Check virus scanning (example with ClamAV) if (extension_loaded('clamav')) { $result = cl_scanfile($file->getRealPath()); if ($result !== CL_CLEAN) { throw new \Exception('File failed security scan'); } } // Log upload attempt \Log::info('File upload started', [ 'filename' => $file->getClientOriginalName(), 'size' => $file->getSize(), 'user_id' => auth()->id() ]); }, 'after_upload' => function($media) { // Send notification $user = auth()->user(); $user->notify(new \App\Notifications\FileUploaded($media)); // Update user storage quota \App\Models\UserQuota::where('user_id', $user->id) ->increment('used_space', $media->size); // Process image with custom logic if (str_starts_with($media->mime_type, 'image/')) { // Generate additional conversions $media->addMediaConversion('large') ->width(1920) ->height(1080) ->performOnCollections('medialibrary'); } // Log successful upload \Log::info('File uploaded successfully', [ 'media_id' => $media->id, 'filename' => $media->file_name ]); }, 'access_check' => function($user, $media) { // Custom access control logic if ($user->hasRole('admin')) { return true; } // Check if user owns the file if ($media->getCustomProperty('user_id') === $user->id) { return true; } // Check shared access $sharedUsers = $media->getCustomProperty('shared_with', []); if (in_array($user->id, $sharedUsers)) { return true; } return false; } ] ]; ``` ## Programmatic Folder Management ### Create and Manage Folders Programmatically ```php $projectName, 'slug' => Str::slug($projectName), 'parent_id' => null, 'user_id' => auth()->id() ]); // Create subfolders $subfolders = ['Documents', 'Images', 'Videos', 'Archives']; foreach ($subfolders as $folderName) { Folder::create([ 'name' => $folderName, 'slug' => Str::slug($folderName), 'parent_id' => $projectFolder->id, 'user_id' => auth()->id() ]); } return $projectFolder; } public function moveFiles($fileIds, $targetFolderId) { $targetFolder = Folder::findOrFail($targetFolderId); foreach ($fileIds as $fileId) { $media = \LivewireFilemanager\Filemanager\Models\Media::find($fileId); if ($media && $this->canMoveFile($media, $targetFolder)) { $media->model_id = $targetFolderId; $media->save(); } } } public function getFolderSize($folderId) { $folder = Folder::with('media')->findOrFail($folderId); $size = $folder->getMedia('medialibrary')->sum('size'); // Add sizes of child folders recursively foreach ($folder->children as $child) { $size += $this->getFolderSize($child->id); } return $size; } private function canMoveFile($media, $targetFolder) { // Check permissions if (config('livewire-fileuploader.acl_enabled')) { return $media->getCustomProperty('user_id') === auth()->id(); } return true; } } ``` ## Error Handling and Validation ### API Error Responses ```bash # Validation error example curl -X POST "https://your-domain.com/api/filemanager/v1/folders" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -d '{ "parent_id": 1 }' ``` ```json { "message": "The name field is required.", "errors": { "name": [ "The name field is required." ] } } ``` ```bash # Unauthorized access example curl -X DELETE "https://your-domain.com/api/filemanager/v1/files/10" \ -H "Accept: application/json" \ -H "Authorization: Bearer INVALID_TOKEN" ``` ```json { "message": "Unauthenticated." } ``` ```bash # File not found example curl -X GET "https://your-domain.com/api/filemanager/v1/files/999" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` ```json { "message": "File not found" } ``` ```bash # Rate limit exceeded example # After exceeding 100 requests per minute curl -X GET "https://your-domain.com/api/filemanager/v1/folders" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` ```json { "message": "Too Many Requests" } ``` ## Internationalization ### Set Application Language ```php 'Upload Files', 'create_folder' => 'New Folder', 'delete' => 'Delete', 'rename' => 'Rename', 'search' => 'Search files and folders...', 'empty' => 'This folder is empty', 'folder_without_title' => 'Untitled Folder', 'folder_already_exists' => 'A folder with this name already exists', 'validation' => [ 'folder_name_required' => 'Folder name is required', 'max_folder_depth_exceeded' => 'Maximum folder depth of :max levels exceeded', ], ]; ``` ## Summary and Use Cases Livewire Filemanager serves as a production-ready solution for a wide range of file management scenarios in Laravel applications. Common use cases include document management systems where users need to organize contracts, invoices, and reports in a hierarchical folder structure; content management systems requiring media library functionality with thumbnail generation and bulk upload capabilities; multi-tenant applications where ACL ensures strict data isolation between users; and API-driven platforms where external services need programmatic access to upload, retrieve, and manage files. The package excels in environments requiring user-friendly interfaces combined with developer-friendly APIs, making it suitable for both end-user applications and system integrations. The package's architecture promotes seamless integration with existing Laravel applications through its service provider pattern, middleware support, and policy-based authorization. Developers can customize behavior through configuration callbacks, extend the UI by publishing views, and implement custom access control logic through policies. The combination of Livewire for reactive interfaces, Spatie Media Library for robust file handling, and Laravel Sanctum for API authentication creates a comprehensive ecosystem that handles everything from small single-user applications to large multi-tenant platforms. With support for queue-based thumbnail generation, automatic slug generation for SEO-friendly URLs, and extensive language support, the package provides enterprise-grade features while maintaining Laravel's characteristic simplicity and elegance.