# 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
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.