├── .version ├── src ├── Contracts │ └── .gitkeep ├── Exceptions │ └── .gitkeep ├── Http │ ├── Requests │ │ └── .gitkeep │ └── Controllers │ │ ├── Controller.php │ │ ├── BrowseController.php │ │ ├── UploadController.php │ │ ├── backup-code.txt │ │ ├── DownloadController.php │ │ ├── FileGroupController.php │ │ ├── FileController.php │ │ └── DirectoryController.php ├── FileManager.php ├── Traits │ ├── RouteKeyNameUUID.php │ ├── HasFile.php │ ├── HasUUID.php │ ├── ErrorHandler.php │ ├── GetConstantsEnum.php │ └── ApiResponder.php ├── Events │ ├── File │ │ ├── Moving.php │ │ ├── Renaming.php │ │ ├── AfterMove.php │ │ ├── BeforeMove.php │ │ ├── AfterRename.php │ │ └── BeforeRename.php │ ├── Image │ │ ├── Resizing.php │ │ ├── AfterResize.php │ │ └── BeforeResize.php │ ├── Upload │ │ ├── AfterUpload.php │ │ ├── Uploading.php │ │ └── BeforeUpload.php │ └── Event.php ├── Facades │ └── FileManagerFacade.php ├── Enums │ ├── FileStatusEnum.php │ ├── DirectoryStatusEnum.php │ └── FileTypeEnum.php ├── Jobs │ ├── Job.php │ ├── UploadFileProcess.php │ └── UploadImageProcess.php ├── Services │ ├── LoggerService.php │ ├── FileGroupService.php │ ├── Downloader.php │ ├── MediaStream.php │ ├── FileService.php │ ├── ArchiveService.php │ ├── DirectoryService.php │ ├── Service.php │ ├── UploadService.php │ └── ImageService.php ├── Models │ ├── FileGroup.php │ ├── Directory.php │ └── File.php ├── Console │ └── Commands │ │ ├── InitializePackageCommand.php │ │ └── InstallPackageCommand.php ├── Providers │ └── FileManagerServiceProvider.php └── helpers.php ├── resources ├── assets │ └── .gitkeep └── lang │ ├── ar │ └── messages.php │ ├── en │ └── messages.php │ ├── fa │ └── messages.php │ └── tr │ └── messages.php ├── .github └── FUNDING.yml ├── routes ├── filemanager-web.php └── filemanger-api.php ├── CONTRIBUTING.md ├── CHANGELOG.md ├── .gitignore ├── test └── FileTest.php ├── composer.json ├── todo.txt ├── frontend └── test.blade.php ├── README-en.md ├── database └── migrations │ └── create_filemanager_tables.php.stub ├── config └── filemanager.php └── README.md /.version: -------------------------------------------------------------------------------- 1 | v0.5.8 2 | -------------------------------------------------------------------------------- /src/Contracts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Exceptions/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Http/Requests/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ['https://idpay.ir/laravelir'] 2 | -------------------------------------------------------------------------------- /routes/filemanager-web.php: -------------------------------------------------------------------------------- 1 | morphMany(File::class); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Events/File/Moving.php: -------------------------------------------------------------------------------- 1 | uuid = (string)Uuid::generate(4); 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Traits/ErrorHandler.php: -------------------------------------------------------------------------------- 1 | errors, $error); 14 | } 15 | 16 | public function getErrors() 17 | { 18 | return $this->errors; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | '', 20 | 'files' => '', 21 | ]; 22 | 23 | return $this->responseSuccess($data); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Events/Event.php: -------------------------------------------------------------------------------- 1 | loggerService = resolve(LoggerService::class); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Enums/FileTypeEnum.php: -------------------------------------------------------------------------------- 1 | getConstants(); 13 | } 14 | 15 | public static function getConstantValues() 16 | { 17 | $reflectionClass = new ReflectionClass(static::class); // __CLASS__ 18 | return array_values($reflectionClass->getConstants()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Jobs/Job.php: -------------------------------------------------------------------------------- 1 | file = $file; 20 | $this->uploadService = new UploadService(); 21 | } 22 | 23 | public function handle() 24 | { 25 | $this->uploadService->uploadFile($this->file); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Jobs/UploadImageProcess.php: -------------------------------------------------------------------------------- 1 | file = $file; 20 | $this->uploadService = new UploadService(); 21 | } 22 | 23 | public function handle() 24 | { 25 | $this->uploadService->uploadImage($this->file); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Services/LoggerService.php: -------------------------------------------------------------------------------- 1 | channel = config('filemanager.logger.channel'); 14 | } 15 | 16 | public function log($msg, $level = 'debug') 17 | { 18 | if (!$this->checkLevel($level)) return false; 19 | 20 | Log::channel($this->channel)->$level($msg); 21 | } 22 | 23 | private function checkLevel($level) 24 | { 25 | $levels = ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug']; 26 | return in_array($level, $levels); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Models/FileGroup.php: -------------------------------------------------------------------------------- 1 | belongsToMany(File::class, 'file_group_pivot'); 24 | } 25 | 26 | public function __toString() 27 | { 28 | return "name: {$this->name}, size: {$this->size}, mime: {$this->mimeType}"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Traits/ApiResponder.php: -------------------------------------------------------------------------------- 1 | json([ 11 | 'success' => true, 12 | 'status_code' => $statusCode, 13 | 'status_message' => $statusMessage, 14 | 'data' => $data 15 | ], $statusCode); 16 | } 17 | 18 | protected function responseError($data, $statusCode = 500, $statusMessage = "Error") 19 | { 20 | return response()->json([ 21 | 'success' => false, 22 | 'status_code' => $statusCode, 23 | 'status_message' => $statusMessage, 24 | 'data' => $data 25 | ], $statusCode); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Http/Controllers/UploadController.php: -------------------------------------------------------------------------------- 1 | uploadService = $uploadService; 18 | } 19 | 20 | public function upload(Request $request) 21 | { 22 | $file = $request->file('file'); 23 | 24 | // UploadFileProcess::dispatch($file); 25 | 26 | return $this->uploadService->uploadImage($file, 1); 27 | } 28 | 29 | public function uploadFiles(Request $request) 30 | { 31 | // 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Models/Directory.php: -------------------------------------------------------------------------------- 1 | belongsTo(Directory::class, 'parent_id', 'id'); 23 | } 24 | 25 | // who created ? 26 | public function user() 27 | { 28 | return $this->belongsTo(config('filemanager.database.user_model'), 'user_id'); 29 | } 30 | 31 | public function __toString() 32 | { 33 | return "name: {$this->name}, size: {$this->size}, mime: {$this->mimeType}"; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Http/Controllers/backup-code.txt: -------------------------------------------------------------------------------- 1 | public function getDirectoryBreadcrumb(Request $request) 2 | { 3 | // This probably could be better. 4 | $id = $request->input('folder'); 5 | 6 | if ($id != 0) { 7 | // Get the current folder 8 | $folders = Directory::where('id', $id)->get(); 9 | 10 | // See if it has a parent 11 | $parentId = $folders[0]["parent_folder"]; 12 | 13 | if ($parentId != 0) { 14 | $looping = true; 15 | 16 | while ($looping) { 17 | // Get the parent details. 18 | $nextDirectory = Directory::where('id', $parentId)->get(); 19 | 20 | $parentId = $nextDirectory[0]["parent_folder"]; 21 | 22 | $folders = $folders->merge($nextDirectory); 23 | 24 | $looping = $parentId != 0; 25 | } 26 | } 27 | 28 | return $folders->toJson(); 29 | } 30 | 31 | return null; 32 | } 33 | -------------------------------------------------------------------------------- /test/FileTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('10 B', File::getHumanReadableSize(10)); 14 | $this->assertEquals('100 B', File::getHumanReadableSize(100)); 15 | $this->assertEquals('1000 B', File::getHumanReadableSize(1000)); 16 | $this->assertEquals('9.77 KB', File::getHumanReadableSize(10000)); 17 | $this->assertEquals('976.56 KB', File::getHumanReadableSize(1000000)); 18 | $this->assertEquals('9.54 MB', File::getHumanReadableSize(10000000)); 19 | $this->assertEquals('9.31 GB', File::getHumanReadableSize(10000000000)); 20 | } 21 | 22 | /** @test */ 23 | public function it_can_determine_the_mime_type_of_a_file() 24 | { 25 | $this->assertEquals('text/x-php', File::getMimeType(__FILE__)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Console/Commands/InitializePackageCommand.php: -------------------------------------------------------------------------------- 1 | disk = Storage::disk(config('filemanager.disk')); 24 | $this->base_directory = config('filemanager.base_directory'); 25 | 26 | } 27 | 28 | public function handle() 29 | { 30 | $this->line("\n... Initialize package ...\n\n"); 31 | 32 | if (!$this->disk->exists($this->base_directory)) { 33 | $this->disk->makeDirectory($this->base_directory, 755); 34 | } 35 | 36 | $this->line("Initialized ... \n"); 37 | 38 | $this->warn("enjoy it, star me of github :) \n"); 39 | $this->info("\t\tGood Luck."); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Http/Controllers/DownloadController.php: -------------------------------------------------------------------------------- 1 | fileModel = $file; 20 | } 21 | 22 | public function download(File $file) 23 | { 24 | $path = $file->path . DIRECTORY_SEPARATOR . $file->name; 25 | 26 | return Storage::disk($file->disk)->download($path, $file->name); 27 | } 28 | 29 | public function downloadFile($uuid) 30 | { 31 | $file = $this->fileModel->where('uuid', $uuid)->first(); 32 | 33 | $secret = env('APP_KEY'); 34 | 35 | $hash = $secret . $file->uuid . getUserIP() . request('t'); 36 | 37 | if ((Carbon::createFromTimestamp(request('t')) > Carbon::now()) && 38 | Hash::check($hash, request('mac')) 39 | ) { 40 | return response()->download(); 41 | } else { 42 | throw new InternalErrorException("link not valid"); 43 | } 44 | 45 | return response()->download(storage_path("app/uploads/") . $file->file_hash, $file->file_name); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Http/Controllers/FileGroupController.php: -------------------------------------------------------------------------------- 1 | fileGroupService = $fileGroupService; 18 | } 19 | 20 | public function index() 21 | { 22 | $fileGroups = $this->fileGroupService->allFileGroups(); 23 | return $this->responseSuccess($fileGroups); 24 | } 25 | 26 | public function store(Request $request) 27 | { 28 | $fileGroups = $this->fileGroupService->createFileGroup($request->only(['title', 'description'])); 29 | return $this->responseSuccess("FileGroup Created"); 30 | } 31 | 32 | public function update(FileGroup $fileGroup, Request $request) 33 | { 34 | $fileGroups = $this->fileGroupService->updateFileGroup($fileGroup, $request->only(['title', 'description'])); 35 | return $this->responseSuccess("FileGroup Updated"); 36 | } 37 | 38 | public function delete(FileGroup $fileGroup) 39 | { 40 | $fileGroups = $this->fileGroupService->deleteFileGroup($fileGroup); 41 | return $this->responseSuccess("FileGroup Deleted"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Services/FileGroupService.php: -------------------------------------------------------------------------------- 1 | model = new FileGroup(); 19 | } 20 | 21 | public function allFileGroups() 22 | { 23 | return $this->model->all(); 24 | } 25 | 26 | public function createFileGroup(array $data) 27 | { 28 | DB::transaction(function () use ($data) { 29 | $fileGroup = $this->model->create([ 30 | 'title' => $data['title'], 31 | 'description' => $data['description'], 32 | ]); 33 | }); 34 | 35 | return true; 36 | } 37 | 38 | public function updateFileGroup(FileGroup $fileGroup, $data) 39 | { 40 | DB::transaction(function () use ($fileGroup, $data) { 41 | $fileGroup->update([ 42 | 'title' => $data['title'], 43 | 'description' => $data['description'], 44 | ]); 45 | }); 46 | 47 | return true; 48 | } 49 | 50 | public function deleteFileGroup(FileGroup $fileGroup) 51 | { 52 | if ($fileGroup->delete()) 53 | return true; 54 | 55 | return false; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miladimos/laravel-filemanager", 3 | "description": "a powerful, flexible laravel file manager", 4 | "homepage": "https://github.com/miladimos/laravel-filemanager", 5 | "type": "library", 6 | "version": "0.6.4", 7 | "keywords": [ 8 | "laravel", 9 | "laravel-filemanager", 10 | "laravel support", 11 | "lumen packages", 12 | "laravel packages", 13 | "lumen support", 14 | "file uploader", 15 | "uploader", 16 | "file manager" 17 | ], 18 | "authors": [ 19 | { 20 | "name": "miladimos", 21 | "email": "miladimos@outlook.com", 22 | "role": "maintaner", 23 | "homepage": "https://github.com/miladimos" 24 | } 25 | ], 26 | "autoload": { 27 | "psr-4": { 28 | "Miladimos\\FileManager\\": "src/" 29 | }, 30 | "files": [ 31 | "src/helpers.php" 32 | ] 33 | }, 34 | "extra": { 35 | "laravel": { 36 | "providers": [ 37 | "Miladimos\\FileManager\\Providers\\FileManagerServiceProvider" 38 | ], 39 | "aliases": { 40 | "FileManager": "Miladimos\\FileManager\\Facades\\FileManagerFacade" 41 | } 42 | } 43 | }, 44 | "require": { 45 | "php": ">=7.4|^8", 46 | "nesbot/carbon": "^2.50.0", 47 | "morilog/jalali": ">=v3.1.0", 48 | "intervention/image": "^2.6", 49 | "ramsey/uuid": "^4.1", 50 | "webpatser/laravel-uuid": "^4", 51 | "league/mime-type-detection": "^1.7" 52 | }, 53 | "minimum-stability": "stable", 54 | "license": "MIT" 55 | } 56 | -------------------------------------------------------------------------------- /src/Services/Downloader.php: -------------------------------------------------------------------------------- 1 | getTempFile($url); 36 | $this->guardAgainstInvalidMimeType($temporaryFile, $allowedMimeTypes); 37 | 38 | $filename = basename(parse_url($url, PHP_URL_PATH)); 39 | $filename = urldecode($filename); 40 | 41 | if ($filename === '') { 42 | $filename = 'file'; 43 | } 44 | 45 | if (!Str::contains($filename, '.')) { 46 | $mediaExtension = explode('/', mime_content_type($temporaryFile)); 47 | $filename = "{$filename}.{$mediaExtension[1]}"; 48 | } 49 | 50 | return app(FileAdderFactory::class) 51 | ->create($this, $temporaryFile) 52 | ->usingName(pathinfo($filename, PATHINFO_FILENAME)) 53 | ->usingFileName($filename); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | add logger / Log Service 2 | 3 | add error handler - error type and codes 4 | 5 | add ACL / advanced permissions ( maybe custom middleware or whatever ) 6 | 7 | add transfer service between storages 8 | 9 | add jobs for upload, image resize, optimizer and ... 10 | 11 | add change visibility : public / private 12 | 13 | add notification system to specific users ( send email/sms to admin users) for specific operations 14 | 15 | add zip / tar comperes algorithms and download or store in current path file or dirs in zip / tar format 16 | Archives create/extract (zip, rar, 7z, tar, gzip, bzip2) 17 | 18 | add work with remote (ssh, ftp, sftp, dropbox) storages 19 | 20 | add pagination system 21 | 22 | add scheduled backup (store or send to ftp, email, ...) 23 | 24 | upload from url 25 | 26 | Chunked file upload for large file 27 | 28 | add statistics for all of activities 29 | 30 | Rich context menu and toolbar 31 | 32 | add auto watermark for images,videos 33 | 34 | add caching system 35 | 36 | ///// Frontend 37 | 38 | add code highlight 39 | 40 | Quick look, preview for common file types 41 | 42 | add advance filters, search 43 | 44 | add hide or visible items 45 | 46 | add breadcrumb manager 47 | 48 | multiple themes 49 | 50 | add frontend with livewire or ... 51 | 52 | https://www.bootdey.com/snippets/tagged/file-manager 53 | 54 | https://pixinvent.com/demo/vuexy-html-bootstrap-admin-template/html/rtl/vertical-menu-template/app-file-manager.html 55 | 56 | http://www.prepbootstrap.com/bootstrap-template/file-manager 57 | 58 | https://themeon.net/nifty/v2.9/app-file-manager.html 59 | 60 | https://demos.devexpress.com/MVCxFileManagerAndUploadDemos/FileManager/Templates 61 | 62 | https://dhtmlx.com/docs/products/demoApps/dhtmlxFileExplorerDemo/ 63 | 64 | https://www.s-vfu.ru/stud/template/file-manager.html 65 | 66 | https://www.s-vfu.ru/stud/template/file-manager.html 67 | 68 | https://github.com/alexusmai/laravel-file-manager 69 | 70 | 71 | -------------------------------------------------------------------------------- /frontend/test.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Test File manager features 9 | 10 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 |
22 | @csrf 23 |
24 | 25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 |
34 | 35 | 36 | 39 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /routes/filemanger-api.php: -------------------------------------------------------------------------------- 1 | 'directories.'], function () { 16 | // Route::get('directories', [DirectoryController::class, 'allDirectory'])->name('index'); 17 | Route::post('/directories/{directory}/parent', [DirectoryController::class, 'getParentDirectory'])->name('parent'); 18 | 19 | Route::post('directories', [DirectoryController::class, 'createDirectory'])->name('create'); 20 | Route::delete('directories/{directory}', [DirectoryController::class, 'deleteDirectories'])->name('delete'); 21 | Route::post('directories/{directory}/rename', [DirectoryController::class, 'renameDirectory'])->name('rename'); 22 | }); 23 | 24 | Route::group(['as' => 'files.'], function () { 25 | Route::delete('files/{file}', [FileController::class, 'deleteFile'])->name('delete'); 26 | Route::post('files/{file}/rename', [FileController::class, 'renameFile'])->name('rename'); 27 | Route::post('files/{file}/{directory}/move', [FileController::class, 'moveFile'])->name('move'); 28 | }); 29 | 30 | Route::group(['as' => 'uploads.'], function () { 31 | Route::post('upload', [UploadController::class, 'upload'])->name('upload'); 32 | }); 33 | 34 | Route::group(['as' => 'downloads.'], function () { 35 | //download/$file->uuid?mac=$hash&t=$timestamp 36 | Route::get("download/{file}?max={hash}&et={timestamp}", [DownloadController::class, 'download'])->name('download'); 37 | }); 38 | 39 | Route::group(['as' => 'filegroups.'], function () { 40 | Route::get('filegroups', [FileGroupController::class, 'index'])->name('index'); 41 | Route::post('filegroups', [FileGroupController::class, 'store'])->name('store'); 42 | Route::put('filegroups/{filegroup}/update', [FileGroupController::class, 'update'])->name('update'); 43 | Route::delete('filegroups/{filegroup}', [FileGroupController::class, 'delete'])->name('delete'); 44 | }); 45 | -------------------------------------------------------------------------------- /src/Http/Controllers/FileController.php: -------------------------------------------------------------------------------- 1 | fileService = $fileService; 18 | } 19 | 20 | public function renameFile(Request $request) 21 | { 22 | $file = $request->file; 23 | $new_name = $request->new_name; 24 | 25 | if ($this->fileService->rename($file, $new_name)) { 26 | return $this->responseSuccess("File Renamed"); 27 | } 28 | 29 | return $this->responseError("Error in Rename File"); 30 | } 31 | 32 | public function moveFile(Request $request) 33 | { 34 | $new_dir = $request->input('new_dir'); // id 35 | $file = $request->input('file'); 36 | 37 | if ($this->fileService->moveFile($file, $new_dir)) { 38 | return $this->responseSuccess("File Moved"); 39 | } 40 | 41 | return $this->responseError("Error in Move File"); 42 | } 43 | 44 | public function deleteFile(File $file) 45 | { 46 | if ($this->fileService->deleteFile($file)) { 47 | return $this->responseSuccess("File Deleted"); 48 | } 49 | 50 | return $this->responseError("Error in Delete File"); 51 | } 52 | 53 | public function deleteFiles(Request $request) 54 | { 55 | if (is_array($request->input('files'))) { 56 | if ($this->fileService->deleteFiles($request->input('files'))) { 57 | return $this->responseError("All files Deleted."); 58 | } 59 | } 60 | 61 | return $this->responseError("files input is not array."); 62 | } 63 | 64 | public function getUserFiles(Request $request) 65 | { 66 | $user = $request->user_id; 67 | $directory = $request->has('directory') ? $request->directory : 0; 68 | 69 | if ($files = $this->fileService->getUserFiles($user, $directory)) { 70 | return $this->responseError($files); 71 | } 72 | 73 | return $this->responseError("Error in get user files."); 74 | } 75 | 76 | public function listAllFiles(Request $request) 77 | { 78 | // 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Console/Commands/InstallPackageCommand.php: -------------------------------------------------------------------------------- 1 | line("\t... Welcome To File Manager Package Installer ..."); 19 | 20 | //config 21 | if (File::exists(config_path('filemanager.php'))) { 22 | $confirm = $this->confirm("filemanager.php already exist. Do you want to overwrite?"); 23 | if ($confirm) { 24 | $this->publishConfig(); 25 | } else { 26 | $this->error("you must overwrite config file"); 27 | exit; 28 | } 29 | } else { 30 | $this->publishConfig(); 31 | } 32 | 33 | // publish migration 34 | $this->publishMigration(); 35 | 36 | 37 | 38 | $this->info("FileManager Package Successfully Installed. Star me on Github :) \n"); 39 | $this->info("\t\tGood Luck."); 40 | } 41 | 42 | private function publishConfig() 43 | { 44 | $this->call('vendor:publish', [ 45 | '--provider' => "Miladimos\FileManager\Providers\FileManagerServiceProvider", 46 | '--tag' => 'filemanager_config', 47 | '--force' => true 48 | ]); 49 | } 50 | 51 | private function publishMigration() 52 | { 53 | $this->call('vendor:publish', [ 54 | '--provider' => "Miladimos\FileManager\Providers\FileManagerServiceProvider", 55 | '--tag' => 'filemanager-migrations', 56 | '--force' => true 57 | ]); 58 | } 59 | 60 | // //assets 61 | // if (File::exists(public_path('filemanager'))) { 62 | // $confirm = $this->confirm("filemanager directory already exist. Do you want to overwrite?"); 63 | // if ($confirm) { 64 | // $this->publishAssets(); 65 | // $this->info("assets overwrite finished"); 66 | // } else { 67 | // $this->info("skipped assets publish"); 68 | // } 69 | // } else { 70 | // $this->publishAssets(); 71 | // $this->info("assets published"); 72 | // } 73 | 74 | // private function publishAssets() 75 | // { 76 | // $this->call('vendor:publish', [ 77 | // '--provider' => "Miladimos\FileManager\Providers\FileManagerServiceProvider", 78 | // '--tag' => 'assets', 79 | // '--force' => true 80 | // ]); 81 | // } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /resources/lang/ar/messages.php: -------------------------------------------------------------------------------- 1 | "Directory created", 5 | 'directory_active' => "Directory Is Active", 6 | 'directory_hide' => "Directory Is Hide", 7 | 'directory_locked' => "Directory is Locked", 8 | 'directory_private' => "Directory is Private", 9 | 'title' => 'File Manager', 10 | 'upload' => 'Upload', 11 | 'upload-message' => 'Drop files here to upload', 12 | 'success-upload' => 'Successfully uploaded!', 13 | 'search' => 'Search', 14 | 'doc' => 'Document', 15 | 'bulk-delete' => 'Bulk Del', 16 | 'preview' => 'Preview', 17 | 'name' => 'Name', 18 | 'size' => 'Size', 19 | 'type' => 'Type', 20 | 'mime' => 'Mime Type', 21 | 'extention' => 'Extention', 22 | 'modified' => 'Modified', 23 | 'created' => 'Created', 24 | 'dimension' => 'Dimension', 25 | 'action' => 'Action', 26 | 'bulk-select' => 'Bulk Select', 27 | 'showing-files' => 'Showing :current of :total files', 28 | 'edit' => 'Edit', 29 | 'update' => 'Update', 30 | 'convert' => 'Convert', 31 | 'format' => 'Format', 32 | 'choose_format' => 'Choose Format', 33 | "filemanager" => "SingleQuote FileManager", 34 | "files" => "Files", 35 | "folders" => "Folders", 36 | "my drive" => "My drive", 37 | "shared with me" => "Shared with me", 38 | "public" => "Public", 39 | "files uploading" => "Files uploading", 40 | "upload files" => "Upload files", 41 | "delete" => "Delete", 42 | "you are not allowed here" => "You are not allowed here", 43 | "this looks empty" => "This looks empty", 44 | "rename" => "Rename", 45 | "cancel" => "Cancel", 46 | "public drive" => "Public drive", 47 | "add folder" => "Add folder", 48 | "new folder" => "New folder", 49 | "folder name" => "Folder name", 50 | "details" => "Details", 51 | "filename" => "Filename", 52 | "created at" => "Created at", 53 | "last updated" => "Last updated", 54 | "mimetype" => "Mimetype", 55 | "extension" => "Extension", 56 | "uploader" => "Uploader", 57 | "unknown" => "Unknown", 58 | "enter email of the user(s)" => "Enter email of the user(s)", 59 | "share" => "Share", 60 | "share file" => "Share file", 61 | 'share folder' => "Share folder", 62 | "shared drive" => "Shared drive", 63 | "or anyone with this link" => "Or anyone with this link", 64 | "load more" => "Load more results", 65 | "storage" => "Storage", 66 | "shared with" => "Shared with", 67 | "remove the shared links" => "Remove the shared links", 68 | "can open" => "Can open", 69 | "can edit" => "Can edit", 70 | "can delete" => "Can delete", 71 | "can upload" => "Can upload", 72 | "permissions" => "Permissions", 73 | "open" => "Open" 74 | ]; 75 | -------------------------------------------------------------------------------- /resources/lang/en/messages.php: -------------------------------------------------------------------------------- 1 | "Directory created", 5 | 'directory_active' => "Directory Is Active", 6 | 'directory_hide' => "Directory Is Hide", 7 | 'directory_locked' => "Directory is Locked", 8 | 'directory_private' => "Directory is Private", 9 | 'title' => 'File Manager', 10 | 'upload' => 'Upload', 11 | 'upload-message' => 'Drop files here to upload', 12 | 'success-upload' => 'Successfully uploaded!', 13 | 'search' => 'Search', 14 | 'doc' => 'Document', 15 | 'bulk-delete' => 'Bulk Del', 16 | 'preview' => 'Preview', 17 | 'name' => 'Name', 18 | 'size' => 'Size', 19 | 'type' => 'Type', 20 | 'mime' => 'Mime Type', 21 | 'extention' => 'Extention', 22 | 'modified' => 'Modified', 23 | 'created' => 'Created', 24 | 'dimension' => 'Dimension', 25 | 'action' => 'Action', 26 | 'bulk-select' => 'Bulk Select', 27 | 'showing-files' => 'Showing :current of :total files', 28 | 'edit' => 'Edit', 29 | 'update' => 'Update', 30 | 'convert' => 'Convert', 31 | 'format' => 'Format', 32 | 'choose_format' => 'Choose Format', 33 | "filemanager" => "SingleQuote FileManager", 34 | "files" => "Files", 35 | "folders" => "Folders", 36 | "my drive" => "My drive", 37 | "shared with me" => "Shared with me", 38 | "public" => "Public", 39 | "files uploading" => "Files uploading", 40 | "upload files" => "Upload files", 41 | "delete" => "Delete", 42 | "you are not allowed here" => "You are not allowed here", 43 | "this looks empty" => "This looks empty", 44 | "rename" => "Rename", 45 | "cancel" => "Cancel", 46 | "public drive" => "Public drive", 47 | "add folder" => "Add folder", 48 | "new folder" => "New folder", 49 | "folder name" => "Folder name", 50 | "details" => "Details", 51 | "filename" => "Filename", 52 | "created at" => "Created at", 53 | "last updated" => "Last updated", 54 | "mimetype" => "Mimetype", 55 | "extension" => "Extension", 56 | "uploader" => "Uploader", 57 | "unknown" => "Unknown", 58 | "enter email of the user(s)" => "Enter email of the user(s)", 59 | "share" => "Share", 60 | "share file" => "Share file", 61 | 'share folder' => "Share folder", 62 | "shared drive" => "Shared drive", 63 | "or anyone with this link" => "Or anyone with this link", 64 | "load more" => "Load more results", 65 | "storage" => "Storage", 66 | "shared with" => "Shared with", 67 | "remove the shared links" => "Remove the shared links", 68 | "can open" => "Can open", 69 | "can edit" => "Can edit", 70 | "can delete" => "Can delete", 71 | "can upload" => "Can upload", 72 | "permissions" => "Permissions", 73 | "open" => "Open" 74 | ]; 75 | -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | - [![Starts](https://img.shields.io/github/stars/miladimos/laravel-filemanager?style=flat&logo=github)](https://github.com/miladimos/laravel-filemanager/forks) 2 | - [![Forks](https://img.shields.io/github/forks/miladimos/laravel-filemanager?style=flat&logo=github)](https://github.com/miladimos/laravel-filemanager/stargazers) 3 | 4 | - [فارسی](README.md) 5 | 6 | 7 | # laravel-file-uploads 8 | A package for convenient way to upload files to the different storages 9 | 10 | ### Installation 11 | 12 | 1. Run the command below to add this package: 13 | ``` 14 | composer require miladimos/laravel-filemanager 15 | ``` 16 | 17 | 2. Open your config/app.php and add the following to the providers array: 18 | ```php 19 | Miladimos\FileManager\Providers\FileManagerServiceProvider::class 20 | ``` 21 | 22 | 3. Run the command below to install: 23 | ``` 24 | php artisan filemanager:install 25 | ``` 26 | 27 | 28 | ### Configuration 29 | Go to the file 30 | 31 | ```php 32 | config/file_uploads.php; 33 | ``` 34 | 35 | There you have an ability to set: 36 | 37 | 1. default storage to upload file (default is: local) 38 | 2. default image quality (default is: 100) 39 | 3. default folder to put your uploads (default is: public/user-uploads) 40 | 41 | ### Usage 42 | To upload file: 43 | 44 | ``` 45 | public function store(Request $request) 46 | { 47 | // This will upload your file to the default folder of selected in config storage 48 | Uploader::uploadFile($request->file('some_file')); 49 | 50 | // This will upload your file to the given as second parameter path of default storage 51 | Uploader::uploadFile($request->file('some_file'), 'path/to/upload'); 52 | 53 | // This will upload your file to the given storage 54 | Uploader::uploadFile($request->file('some_file'), 'path/to/upload', 'storage_name'); 55 | 56 | // This will also resize image to the given width and height 57 | Uploader::uploadFile($request->file('some_file'), 'path/to/upload', 'storage_name'); 58 | } 59 | ``` 60 | 61 | 62 | To upload base64 string of image: 63 | 64 | ```php 65 | public function store(Request $request) 66 | { 67 | // This will upload your file to the default folder of selected in config storage 68 | Uploader::uploadBase64Image($request->input('image')); 69 | 70 | // This will upload your file to the given as second parameter path of default storage 71 | Uploader::uploadFile($request->input('image'), 'path/to/upload'); 72 | 73 | // This will upload your file to the given storage 74 | Uploader::uploadFile($request->input('image'), 'path/to/upload', 'storage_name'); 75 | 76 | // This will also resize image to the given width and height 77 | Uploader::uploadFile($request->input('image'), 'path/to/upload', 'storage_name'); 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /src/Http/Controllers/DirectoryController.php: -------------------------------------------------------------------------------- 1 | directoryService = $directoryService; 19 | } 20 | 21 | public function createDirectory(Request $request) 22 | { 23 | $data = [ 24 | 'name' => $request->get('name'), 25 | 'description' => $request->has('description') ? $request->input('description') : null, 26 | 'parent_id' => $request->has('parent_id') ? $request->input('parent_id') : 0, 27 | ]; 28 | 29 | if ($this->directoryService->createDirectory($data)) { 30 | $msg = trans('filemanager::messages.directory_created'); 31 | return $this->responseSuccess($msg, 201, "Created"); 32 | } 33 | 34 | return $this->responseError("Error in Directory create", 500); 35 | } 36 | 37 | public function deleteDirectories(Request $request) 38 | { 39 | if (is_array($request->get('directories'))) { 40 | foreach ($request->get('directories', []) as $key => $directory) { 41 | if (!$this->directoryService->deleteDirectory($directory)) { 42 | return $this->responseError("Error in Directory Delete", 500); 43 | } 44 | } 45 | } 46 | 47 | if (!$this->directoryService->deleteDirectory($request->get('directories'))) { 48 | return $this->responseError("Error in Directory Delete", 500); 49 | } 50 | 51 | return $this->responseSuccess("Directories Deleted"); 52 | } 53 | 54 | public function renameDirectory(Directory $directory, Request $request) 55 | { 56 | $name = $request->input('new_name'); 57 | 58 | if (checkInstanceOf($directory, Directory::class)) { 59 | } 60 | 61 | return response()->json(['msg' => 'Directory renamed.', 'status' => '200'], 200); 62 | } 63 | 64 | public function getUserDirectorys(Request $request) 65 | { 66 | $folder = $request->input('folder'); 67 | 68 | if ($folder == 0) { 69 | // get the parent folders 70 | $folders = Directory::where('parent_folder', '0')->where('user_id', Auth::id())->orderBy('folder_name', 'asc')->get(); 71 | } else { 72 | $folders = Directory::where('parent_folder', $folder)->where('user_id', Auth::id())->orderBy('folder_name', 'asc')->get(); 73 | } 74 | 75 | return $folders->toJson(); 76 | } 77 | 78 | public function getParentDirectory(Directory $directory) 79 | { 80 | return $directory->parent(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /database/migrations/create_filemanager_tables.php.stub: -------------------------------------------------------------------------------- 1 | id(); 14 | $table->uuid('uuid')->uniuqe(); 15 | $table->foreignID('parent_id')->nullable(); 16 | $table->foreignID('user_id')->nullable(); 17 | $table->string('disk'); 18 | $table->string('name'); 19 | $table->string('path'); 20 | $table->string('color_hex')->nullable(); 21 | $table->string('description')->nullable(); 22 | $table->string('permission')->nullable(); 23 | $table->char('status')->default('a'); 24 | $table->timestamps(); 25 | }); 26 | 27 | Schema::create('filemanager_files', function (Blueprint $table) { 28 | $table->id(); 29 | $table->uuid('uuid')->uniuqe(); 30 | $table->string("fileable_type")->nullable(); 31 | $table->unsignedBigInteger("fileable_id")->nullable(); 32 | $table->foreignId('directory_id'); 33 | $table->foreignId('user_id')->nullable()->comment('who made'); 34 | $table->string('original_name'); 35 | $table->string('disk'); 36 | $table->string('name'); 37 | $table->string('path'); 38 | $table->string('url'); 39 | $table->string('extension')->nullable(); 40 | $table->string('mime_type')->nullable(); 41 | $table->unsignedBigInteger('size')->nullable()->comment('in bytes'); 42 | $table->char('type')->nullable(); 43 | $table->char('status')->nullable(); 44 | $table->string('width')->nullable(); 45 | $table->string('height')->nullable(); 46 | $table->string('description')->nullable(); 47 | $table->string('permission')->nullable(); 48 | $table->boolean('is_private')->default(false); 49 | $table->unsignedInteger('priority_column')->nullable(); 50 | $table->timestamps(); 51 | }); 52 | 53 | Schema::create('filemanager_file_groups', function (Blueprint $table) { 54 | $table->id(); 55 | $table->uuid('uuid')->uniuqe(); 56 | $table->string('title')->unique(); 57 | $table->string('description')->unique()->nullable(); 58 | $table->boolean('active')->default(true); 59 | $table->timestamps(); 60 | }); 61 | 62 | Schema::create('filemanager_file_group_pivot', function (Blueprint $table) { 63 | $table->id(); 64 | $table->morphs('groupable'); 65 | $table->foreignId('group_id'); 66 | }); 67 | } 68 | 69 | 70 | public function down() 71 | { 72 | Schema::dropIfExists('filemanager_directories'); 73 | 74 | Schema::dropIfExists('filemanager_files'); 75 | 76 | Schema::dropIfExists('filemanager_file_groups'); 77 | 78 | Schema::dropIfExists('filemanager_file_group_pivot'); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Providers/FileManagerServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__ . "/../../config/filemanager.php", 'filemanager'); 17 | 18 | // $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations'); 19 | 20 | $this->registerViews(); 21 | 22 | $this->registerFacades(); 23 | } 24 | 25 | public function boot() 26 | { 27 | if ($this->app->runningInConsole()) { 28 | $this->registerConfig(); 29 | $this->registerMigrations(); 30 | $this->registerCommands(); 31 | $this->registerTranslations(); 32 | } 33 | 34 | $this->registerRoutes(); 35 | } 36 | 37 | private function registerFacades() 38 | { 39 | $this->app->singleton('filemanager', FileManager::class); 40 | } 41 | 42 | private function registerConfig() 43 | { 44 | $this->publishes([ 45 | __DIR__ . '/../../config/filemanager.php' => config_path('filemanager.php') 46 | ], 'filemanager_config'); 47 | } 48 | 49 | private function registerTranslations() 50 | { 51 | $this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'filemanager'); 52 | 53 | $this->publishes([ 54 | __DIR__ . '/../resources/lang' => resource_path('lang/miladimos/laravel-filemanager'), 55 | ]); 56 | } 57 | 58 | private function registerViews() 59 | { 60 | $this->loadViewsFrom(__DIR__ . '/../../frontend', 'filemanager'); 61 | 62 | $this->publishes([ 63 | __DIR__ . '/../../frontend' => resource_path('views/miladimos/laravel-filemanager'), 64 | ]); 65 | } 66 | 67 | private function registerCommands() 68 | { 69 | $this->commands([ 70 | InstallPackageCommand::class, 71 | InitializePackageCommand::class, 72 | ]); 73 | } 74 | 75 | private function registerMigrations() 76 | { 77 | // if (!class_exists('CreateFilemanagerTables')) { 78 | $this->publishes([ 79 | __DIR__ . '/../../database/migrations/create_filemanager_tables.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_filemanager_tables.php'), 80 | // you can add any number of migrations here 81 | ], 'filemanager-migrations'); 82 | // } 83 | } 84 | 85 | private function registerRoutes() 86 | { 87 | Route::group($this->routeConfiguration(), function () { 88 | $this->loadRoutesFrom(__DIR__ . '/../../routes/filemanger-api.php', 'filemanager-routes'); 89 | }); 90 | } 91 | 92 | private function routeConfiguration() 93 | { 94 | $filemanager_api_version = 'V1'; 95 | 96 | return [ 97 | 'prefix' => config('filemanager.routes.api.api_prefix') . '/' . $filemanager_api_version . '/' . config('filemanager.routes.prefix'), 98 | 'middleware' => config('filemanager.routes.api.middleware'), 99 | 'as' => 'filemanager.' 100 | ]; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | check()) { 28 | return false; 29 | } 30 | 31 | return auth($guard)->user(); 32 | } 33 | } 34 | 35 | // if exists return true 36 | if (!function_exists('checkPath')) { 37 | function checkPath($path, $disk) 38 | { 39 | if ($disk && $path && \Illuminate\Support\Facades\Storage::disk($disk)->exists($path)) 40 | return true; 41 | return false; 42 | } 43 | } 44 | 45 | if (!function_exists("getUserIP")) { 46 | function getUserIP() 47 | { 48 | $client = $_SERVER['HTTP_CLIENT_IP']; 49 | $forward = $_SERVER['HTTP_X_FORWARDED_FOR']; 50 | $remote = $_SERVER['REMOTE_ADDR']; 51 | 52 | if (filter_var($client, FILTER_VALIDATE_IP)) { 53 | $ip = $client; 54 | } elseif (filter_var($forward, FILTER_VALIDATE_IP)) { 55 | $ip = $forward; 56 | } else { 57 | $ip = $remote; 58 | } 59 | 60 | return $ip; 61 | } 62 | } 63 | 64 | if (!function_exists('decodeBase64File')) { 65 | function decodeBase64File($encodedFile) 66 | { 67 | // اینجا اطلاعات اضافی رو پاک میکنم تا کد اصلی رو بگیرم 68 | $file = str_replace(' ', '+', $encodedFile); 69 | $file = substr($file, strpos($file, ';base64,') + 8); 70 | $decodedFile = base64_decode($file); 71 | 72 | // با کمک توابع پی اچ پی، مشخصات فایل رو بررسی می کنم 73 | $fileMimeType = finfo_buffer(finfo_open(), $decodedFile, FILEINFO_MIME_TYPE); 74 | $fileExt = substr($fileMimeType, strpos($fileMimeType, '/') + 1); 75 | 76 | return [ 77 | 'file' => $decodedFile, // فایل آماده برای ذخیره سازی در دیسک 78 | 'mime' => $fileMimeType, // نوع فایل 79 | 'ext' => $fileExt, // اکستنشن فایل 80 | 'size' => (int)strlen($decodedFile) // حجم فایل با واحد بایت 81 | ]; 82 | } 83 | } 84 | 85 | if (!function_exists('version')) { 86 | function version(): string 87 | { 88 | return trim(file_get_contents(base_path('.version'))); 89 | } 90 | } 91 | 92 | if (!function_exists('checkInstanceOf')) { 93 | function checkInstanceOf($varable, string $model): string 94 | { 95 | return !!($varable instanceof $model); 96 | } 97 | } 98 | 99 | if (!function_exists('generateDownloadHash')) { 100 | function generateDownloadHash(): string 101 | { 102 | return ''; 103 | } 104 | } 105 | 106 | if (!function_exists('logActive')) { 107 | function logActive(): bool 108 | { 109 | return config('filemanager.logger.active'); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Services/MediaStream.php: -------------------------------------------------------------------------------- 1 | zipName = $zipName; 28 | 29 | $this->mediaItems = collect(); 30 | 31 | $this->zipOptions = new ArchiveOptions(); 32 | } 33 | 34 | public function useZipOptions(callable $zipOptionsCallable): self 35 | { 36 | $zipOptionsCallable($this->zipOptions); 37 | 38 | return $this; 39 | } 40 | 41 | public function addMedia(...$mediaItems): self 42 | { 43 | collect($mediaItems) 44 | ->flatMap(function ($item) { 45 | if ($item instanceof Media) { 46 | return [$item]; 47 | } 48 | 49 | if ($item instanceof Collection) { 50 | return $item->reduce(function (array $carry, Media $media) { 51 | $carry[] = $media; 52 | 53 | return $carry; 54 | }, []); 55 | } 56 | 57 | return $item; 58 | }) 59 | ->each(fn (Media $media) => $this->mediaItems->push($media)); 60 | 61 | return $this; 62 | } 63 | 64 | public function getMediaItems(): Collection 65 | { 66 | return $this->mediaItems; 67 | } 68 | 69 | public function toResponse($request): StreamedResponse 70 | { 71 | $headers = [ 72 | 'Content-Disposition' => "attachment; filename=\"{$this->zipName}\"", 73 | 'Content-Type' => 'application/octet-stream', 74 | ]; 75 | 76 | return new StreamedResponse(fn () => $this->getZipStream(), 200, $headers); 77 | } 78 | 79 | public function getZipStream(): ZipStream 80 | { 81 | $zip = new ZipStream($this->zipName, $this->zipOptions); 82 | 83 | $this->getZipStreamContents()->each(function (array $mediaInZip) use ($zip) { 84 | $stream = $mediaInZip['media']->stream(); 85 | 86 | $zip->addFileFromStream($mediaInZip['fileNameInZip'], $stream); 87 | 88 | if (is_resource($stream)) { 89 | fclose($stream); 90 | } 91 | }); 92 | 93 | $zip->finish(); 94 | 95 | return $zip; 96 | } 97 | 98 | protected function getZipStreamContents(): Collection 99 | { 100 | return $this->mediaItems->map(fn (Media $media, $mediaItemIndex) => [ 101 | 'fileNameInZip' => $this->getZipFileNamePrefix($this->mediaItems, $mediaItemIndex).$this->getFileNameWithSuffix($this->mediaItems, $mediaItemIndex), 102 | 'media' => $media, 103 | ]); 104 | } 105 | 106 | protected function getFileNameWithSuffix(Collection $mediaItems, int $currentIndex): string 107 | { 108 | $fileNameCount = 0; 109 | 110 | $fileName = $mediaItems[$currentIndex]->file_name; 111 | 112 | foreach ($mediaItems as $index => $media) { 113 | if ($index >= $currentIndex) { 114 | break; 115 | } 116 | 117 | if ($this->getZipFileNamePrefix($mediaItems, $index).$media->file_name === $this->getZipFileNamePrefix($mediaItems, $currentIndex).$fileName) { 118 | $fileNameCount++; 119 | } 120 | } 121 | 122 | if ($fileNameCount === 0) { 123 | return $fileName; 124 | } 125 | 126 | $extension = pathinfo($fileName, PATHINFO_EXTENSION); 127 | $fileNameWithoutExtension = pathinfo($fileName, PATHINFO_FILENAME); 128 | 129 | return "{$fileNameWithoutExtension} ({$fileNameCount}).{$extension}"; 130 | } 131 | 132 | protected function getZipFileNamePrefix(Collection $mediaItems, int $currentIndex): string 133 | { 134 | return $mediaItems[$currentIndex]->hasCustomProperty('zip_filename_prefix') ? $mediaItems[$currentIndex]->getCustomProperty('zip_filename_prefix') : ''; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Services/FileService.php: -------------------------------------------------------------------------------- 1 | model = new File(); 24 | } 25 | 26 | public function rename(File $file, $newFileName) 27 | { 28 | 29 | if (!checkPath($file->path, $this->disk_name)) { 30 | return false; 31 | } 32 | 33 | if ($this->disk->move(($file->path . $this->ds . $file->name), $file->path . $this->ds . $newFileName)) { 34 | 35 | DB::transaction(function () use ($file, $newFileName) { 36 | $this->model->where('name', $file->name)->update([ 37 | 'name' => $newFileName, 38 | ]); 39 | }); 40 | 41 | return true; 42 | } 43 | 44 | return false; 45 | } 46 | 47 | public function moveFile(File $file, Directory $newdir) 48 | { 49 | 50 | if (!checkPath($file->path, $this->disk_name)) { 51 | return false; 52 | } 53 | 54 | if ($this->disk->move($file->path, $newdir->path)) { 55 | DB::transaction(function () use ($file, $newdir) { 56 | $this->model->where('id', $file)->update([ 57 | 'directory_id' => $newdir->id, 58 | 'path' => $newdir->path 59 | ]); 60 | }); 61 | } 62 | 63 | return false; 64 | } 65 | 66 | public function copyFile(File $file, Directory $new_dir) 67 | { 68 | 69 | if (!checkPath($file->path, $this->disk_name)) { 70 | return false; 71 | } 72 | 73 | if ($this->disk->copy($file->path, $new_dir->path)) { 74 | DB::transaction(function () use ($file, $new_dir) { 75 | $this->model->where('id', $file)->update([ 76 | 'directory_id' => $new_dir, 77 | 'path' => $new_dir->path 78 | ]); 79 | }); 80 | 81 | return true; 82 | } 83 | 84 | return false; 85 | } 86 | 87 | public function deleteFile(File $file) 88 | { 89 | if (!checkPath($file->path, $this->disk_name)) { 90 | return false; 91 | } 92 | 93 | if ($this->disk->delete($file->path)) { 94 | $file->forceDelete(); 95 | 96 | return true; 97 | } 98 | 99 | return false; 100 | } 101 | 102 | public function getUserFiles($user, $directory) 103 | { 104 | // all files in everywhere 105 | if ($directory == 0) { 106 | $files = $this->model->where('user_id', $user)->latest()->get(); 107 | } else { 108 | $files = $this->model->where('directory_id', $directory)->where('user_id', $user)->latest()->get(); 109 | } 110 | 111 | return $files; 112 | } 113 | 114 | /** 115 | * generate the link for download file 116 | * this link has expire time 117 | * 118 | * @return string 119 | */ 120 | public function generateLink($uuid) 121 | { 122 | $file = File::where('uuid', $uuid)->first(); 123 | 124 | $secret = env('APP_KEY'); 125 | 126 | $expireTime = (int)config('filemanager.download_link_expire'); 127 | 128 | $timestamp = Carbon::now()->addMinutes($expireTime)->timestamp; 129 | $hash = Hash::make($secret . $file->uuid . getUserIP() . $timestamp); 130 | 131 | // return "/api/filemanager/download/$file->uuid?mac=$hash&t=$timestamp"; 132 | return route('filemanager.download', [$file, $hash, $timestamp]); 133 | } 134 | 135 | /** 136 | * Return the full web path to a file. 137 | * 138 | * @param $path 139 | * 140 | * @return string 141 | */ 142 | public function fileWebpath($path) 143 | { 144 | $path = $this->disk->url($path); 145 | // Remove extra slashes from URL without removing first two slashes after http/https:... 146 | $path = preg_replace('/([^:])(\/{2,})/', '$1/', $path); 147 | 148 | return $path; 149 | } 150 | 151 | /** 152 | * @param $path 153 | * 154 | * @return string 155 | */ 156 | private function fileRelativePath($path) 157 | { 158 | $path = $this->fileWebpath($path); 159 | // @todo This wont work for files not located on the current server... 160 | $path = Str::replaceFirst(env('APP_URL'), '', $path); 161 | $path = str_replace(' ', '%20', $path); 162 | 163 | return $path; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /resources/lang/fa/messages.php: -------------------------------------------------------------------------------- 1 | "Directory Is Active", 5 | 'directory_hide' => "Directory Is Hide", 6 | 'directory_locked' => "Directory is Locked", 7 | 'directory_private' => "Directory is Private", 8 | 'directory_created' => "دایرکتوری ایجاد شد", 9 | 'title' => 'مدیریت فایل', 10 | 'upload' => 'آپلود', 11 | 'upload-message' => 'Drop files here to upload', 12 | 'success-upload' => 'با موفقیت آپلود شد', 13 | 'search' => 'جستجو', 14 | 'doc' => 'Document', 15 | 'preview' => 'پیش نمایش', 16 | 'name' => 'نام', 17 | 'size' => 'سایز', 18 | 'type' => 'نوع', 19 | 'mime' => 'Mime Type', 20 | 'extention' => 'پسوند', 21 | 'modified' => 'تغییر کرده', 22 | 'created' => 'ایجاد شده', 23 | 'dimension' => 'Dimension', 24 | 'action' => 'عملیات', 25 | 'showing-files' => 'Showing :current of :total files', 26 | 'edit' => 'ویرایش', 27 | 'update' => 'آپدیت', 28 | 'convert' => 'تبدیل', 29 | 'format' => 'فرمت', 30 | 'choose_format' => 'انتخاب فرمت', 31 | "files" => "فایل ها", 32 | "folders" => "فولدر ها", 33 | "my drive" => "My drive", 34 | "shared with me" => "با من به اشتراک بزار", 35 | "public" => "عمومی", 36 | "files uploading" => "فایل ها در حال آپلود ...", 37 | "upload files" => "آبلود فایل ها", 38 | "delete" => "حذف", 39 | "you are not allowed here" => "You are not allowed here", 40 | "rename" => "تغیر نام", 41 | "cancel" => "انصراف", 42 | "public drive" => "Public drive", 43 | "add folder" => "افزودن دایرکتوری", 44 | "new folder" => "New folder", 45 | "details" => "توضیحات", 46 | "filename" => "نام فایل", 47 | "created at" => "ایجاد شده در", 48 | "last updated" => "آخرین بروزرسانی", 49 | "mimetype" => "Mimetype", 50 | "extension" => "Extension", 51 | "uploader" => "Uploader", 52 | "unknown" => "Unknown", 53 | "enter email of the user(s)" => "Enter email of the user(s)", 54 | "share" => "اشتراک گذاری", 55 | "share file" => "Share file", 56 | 'share folder' => "Share folder", 57 | "shared drive" => "Shared drive", 58 | "or anyone with this link" => "Or anyone with this link", 59 | "load more" => "Load more results", 60 | "storage" => "Storage", 61 | "shared with" => "Shared with", 62 | "remove the shared links" => "Remove the shared links", 63 | "can open" => "Can open", 64 | "can edit" => "Can edit", 65 | "can delete" => "Can delete", 66 | "can upload" => "Can upload", 67 | "permissions" => "سطح دسترسی ها", 68 | "open" => "باز کردن", 69 | 'nav-back' => 'بازگشت', 70 | 'nav-new' => 'پوشه جدید', 71 | 'nav-upload' => 'آپلود', 72 | 'nav-thumbnails' => 'تصویرک ها', 73 | 'nav-list' => 'لیست', 74 | 'nav-sort' => 'مرتب سازی', 75 | 'nav-sort-alphabetic' => 'مرتب سازی الفبایی', 76 | 'nav-sort-time' => 'مرتب سازی زمانی', 77 | 'menu-rename' => 'تغییر نام', 78 | 'menu-delete' => 'حذف', 79 | 'menu-view' => 'مشاهده', 80 | 'menu-download' => 'دانلود', 81 | 'menu-resize' => 'تغییر اندازه', 82 | 'menu-crop' => 'برش', 83 | 'menu-move' => 'انتقال', 84 | 'menu-multiple' => 'انتخاب چندتایی', 85 | 86 | 'title-page' => 'مدیریت فایل', 87 | 'title-panel' => 'مدیریت فایل لاراول', 88 | 'title-upload' => 'آپلود فایل', 89 | 'title-view' => 'مشاهده فایل', 90 | 'title-user' => 'فایل ها', 91 | 'title-share' => 'فایل های اشتراکی', 92 | 'title-item' => 'آیتم', 93 | 'title-size' => 'اندازه', 94 | 'title-type' => 'نوع', 95 | 'title-modified' => 'تاریخ آخرین ویرایش', 96 | 'title-action' => 'اقدام', 97 | 98 | 'type-folder' => 'پوشه', 99 | 100 | 'message-empty' => 'پوشه خالی است.', 101 | 'message-choose' => 'انتخاب فایل', 102 | 'message-delete' => 'آیا از حذف این آیتم مطمئن هستید؟', 103 | 'message-name' => 'نام پوشه:', 104 | 'message-rename' => 'تغییر نام به:', 105 | 'message-extension_not_found' => 'لطفا gd یا imagick را برای برش، تغییر اندازه و ایجاد تصویرک نصب کنید.', 106 | 'message-drop' => 'یا فایل ها را برای آپلود اینجا رها کنید', 107 | 108 | 'error-rename' => 'این نام قبلا استفاده شده!', 109 | 'error-file-name' => 'نام فایل نباید خالی باشد!', 110 | 'error-file-empty' => 'باید یک فایل انتخاب کنید!', 111 | 'error-file-exist' => 'فایلی با این نام از قبل وجود دارد!', 112 | 'error-file-size' => 'محدودیت حجم فایل سرور! (حداکثر حجم: :max)', 113 | 'error-delete-folder' => 'به دلیل خالی نبودن پوشه امکان حذف آن وجود ندارد!', 114 | 'error-folder-name' => 'نام پوشه نمی تواند خالی باشد!', 115 | 'error-folder-exist' => 'پوشه ای با این نام از قبل وجود دارد!', 116 | 'error-folder-alnum' => 'فقط اسامی الفبایی برای پوشه مجاز است!', 117 | 'error-folder-not-found' => 'پوشه‌ای یافت نشد! (:folder)', 118 | 'error-mime' => 'پسوند غیرمجاز: ', 119 | 'error-size' => 'سایز بیش از حد:', 120 | 'error-instance' => 'فایل آپلود شده باید نمونه ای از UploadedFile باشد', 121 | 'error-invalid' => 'درخواست آپلود غیرمعتبر', 122 | 'error-other' => 'خطایی رخ داد: ', 123 | 'error-too-large' => 'درخواست موجودیت خیلی طولانیست!', 124 | 125 | 'btn-upload' => 'آپلود فایل', 126 | 'btn-uploading' => 'در حال آپلود', 127 | 'btn-close' => 'بستن', 128 | 'btn-crop' => 'برش', 129 | 'btn-copy-crop' => 'برش و ذخیره در فایل جدید', 130 | 'btn-crop-free' => 'برش آزاد', 131 | 'btn-cancel' => 'انصراف', 132 | 'btn-confirm' => 'تایید', 133 | 'btn-resize' => 'تغییر اندازه', 134 | 'btn-open' => 'باز کردن پوشه', 135 | 136 | 'resize-ratio' => 'نسبت:', 137 | 'resize-scaled' => 'تصویر مقیاس شده:', 138 | 'resize-true' => 'بله', 139 | 'resize-old-height' => 'ارتفاع اصلی:', 140 | 'resize-old-width' => 'عرض اصلی:', 141 | 'resize-new-height' => 'ارتفاع:', 142 | 'resize-new-width' => 'عرض:', 143 | 144 | 'locale-bootbox' => 'fa', 145 | ]; 146 | -------------------------------------------------------------------------------- /resources/lang/tr/messages.php: -------------------------------------------------------------------------------- 1 | "Directory Is Active", 5 | 'directory_hide' => "Directory Is Hide", 6 | 'directory_locked' => "Directory is Locked", 7 | 'directory_private' => "Directory is Private", 8 | 'directory_created' => "دایرکتوری ایجاد شد", 9 | 'title' => 'مدیریت فایل', 10 | 'upload' => 'آپلود', 11 | 'upload-message' => 'Drop files here to upload', 12 | 'success-upload' => 'با موفقیت آپلود شد', 13 | 'search' => 'جستجو', 14 | 'doc' => 'Document', 15 | 'preview' => 'پیش نمایش', 16 | 'name' => 'نام', 17 | 'size' => 'سایز', 18 | 'type' => 'نوع', 19 | 'mime' => 'Mime Type', 20 | 'extention' => 'پسوند', 21 | 'modified' => 'تغییر کرده', 22 | 'created' => 'ایجاد شده', 23 | 'dimension' => 'Dimension', 24 | 'action' => 'عملیات', 25 | 'showing-files' => 'Showing :current of :total files', 26 | 'edit' => 'ویرایش', 27 | 'update' => 'آپدیت', 28 | 'convert' => 'تبدیل', 29 | 'format' => 'فرمت', 30 | 'choose_format' => 'انتخاب فرمت', 31 | "files" => "فایل ها", 32 | "folders" => "فولدر ها", 33 | "my drive" => "My drive", 34 | "shared with me" => "با من به اشتراک بزار", 35 | "public" => "عمومی", 36 | "files uploading" => "فایل ها در حال آپلود ...", 37 | "upload files" => "آبلود فایل ها", 38 | "delete" => "حذف", 39 | "you are not allowed here" => "You are not allowed here", 40 | "rename" => "تغیر نام", 41 | "cancel" => "انصراف", 42 | "public drive" => "Public drive", 43 | "add folder" => "افزودن دایرکتوری", 44 | "new folder" => "New folder", 45 | "details" => "توضیحات", 46 | "filename" => "نام فایل", 47 | "created at" => "ایجاد شده در", 48 | "last updated" => "آخرین بروزرسانی", 49 | "mimetype" => "Mimetype", 50 | "extension" => "Extension", 51 | "uploader" => "Uploader", 52 | "unknown" => "Unknown", 53 | "enter email of the user(s)" => "Enter email of the user(s)", 54 | "share" => "اشتراک گذاری", 55 | "share file" => "Share file", 56 | 'share folder' => "Share folder", 57 | "shared drive" => "Shared drive", 58 | "or anyone with this link" => "Or anyone with this link", 59 | "load more" => "Load more results", 60 | "storage" => "Storage", 61 | "shared with" => "Shared with", 62 | "remove the shared links" => "Remove the shared links", 63 | "can open" => "Can open", 64 | "can edit" => "Can edit", 65 | "can delete" => "Can delete", 66 | "can upload" => "Can upload", 67 | "permissions" => "سطح دسترسی ها", 68 | "open" => "باز کردن", 69 | 'nav-back' => 'بازگشت', 70 | 'nav-new' => 'پوشه جدید', 71 | 'nav-upload' => 'آپلود', 72 | 'nav-thumbnails' => 'تصویرک ها', 73 | 'nav-list' => 'لیست', 74 | 'nav-sort' => 'مرتب سازی', 75 | 'nav-sort-alphabetic' => 'مرتب سازی الفبایی', 76 | 'nav-sort-time' => 'مرتب سازی زمانی', 77 | 'menu-rename' => 'تغییر نام', 78 | 'menu-delete' => 'حذف', 79 | 'menu-view' => 'مشاهده', 80 | 'menu-download' => 'دانلود', 81 | 'menu-resize' => 'تغییر اندازه', 82 | 'menu-crop' => 'برش', 83 | 'menu-move' => 'انتقال', 84 | 'menu-multiple' => 'انتخاب چندتایی', 85 | 86 | 'title-page' => 'مدیریت فایل', 87 | 'title-panel' => 'مدیریت فایل لاراول', 88 | 'title-upload' => 'آپلود فایل', 89 | 'title-view' => 'مشاهده فایل', 90 | 'title-user' => 'فایل ها', 91 | 'title-share' => 'فایل های اشتراکی', 92 | 'title-item' => 'آیتم', 93 | 'title-size' => 'اندازه', 94 | 'title-type' => 'نوع', 95 | 'title-modified' => 'تاریخ آخرین ویرایش', 96 | 'title-action' => 'اقدام', 97 | 98 | 'type-folder' => 'پوشه', 99 | 100 | 'message-empty' => 'پوشه خالی است.', 101 | 'message-choose' => 'انتخاب فایل', 102 | 'message-delete' => 'آیا از حذف این آیتم مطمئن هستید؟', 103 | 'message-name' => 'نام پوشه:', 104 | 'message-rename' => 'تغییر نام به:', 105 | 'message-extension_not_found' => 'لطفا gd یا imagick را برای برش، تغییر اندازه و ایجاد تصویرک نصب کنید.', 106 | 'message-drop' => 'یا فایل ها را برای آپلود اینجا رها کنید', 107 | 108 | 'error-rename' => 'این نام قبلا استفاده شده!', 109 | 'error-file-name' => 'نام فایل نباید خالی باشد!', 110 | 'error-file-empty' => 'باید یک فایل انتخاب کنید!', 111 | 'error-file-exist' => 'فایلی با این نام از قبل وجود دارد!', 112 | 'error-file-size' => 'محدودیت حجم فایل سرور! (حداکثر حجم: :max)', 113 | 'error-delete-folder' => 'به دلیل خالی نبودن پوشه امکان حذف آن وجود ندارد!', 114 | 'error-folder-name' => 'نام پوشه نمی تواند خالی باشد!', 115 | 'error-folder-exist' => 'پوشه ای با این نام از قبل وجود دارد!', 116 | 'error-folder-alnum' => 'فقط اسامی الفبایی برای پوشه مجاز است!', 117 | 'error-folder-not-found' => 'پوشه‌ای یافت نشد! (:folder)', 118 | 'error-mime' => 'پسوند غیرمجاز: ', 119 | 'error-size' => 'سایز بیش از حد:', 120 | 'error-instance' => 'فایل آپلود شده باید نمونه ای از UploadedFile باشد', 121 | 'error-invalid' => 'درخواست آپلود غیرمعتبر', 122 | 'error-other' => 'خطایی رخ داد: ', 123 | 'error-too-large' => 'درخواست موجودیت خیلی طولانیست!', 124 | 125 | 'btn-upload' => 'آپلود فایل', 126 | 'btn-uploading' => 'در حال آپلود', 127 | 'btn-close' => 'بستن', 128 | 'btn-crop' => 'برش', 129 | 'btn-copy-crop' => 'برش و ذخیره در فایل جدید', 130 | 'btn-crop-free' => 'برش آزاد', 131 | 'btn-cancel' => 'انصراف', 132 | 'btn-confirm' => 'تایید', 133 | 'btn-resize' => 'تغییر اندازه', 134 | 'btn-open' => 'باز کردن پوشه', 135 | 136 | 'resize-ratio' => 'نسبت:', 137 | 'resize-scaled' => 'تصویر مقیاس شده:', 138 | 'resize-true' => 'بله', 139 | 'resize-old-height' => 'ارتفاع اصلی:', 140 | 'resize-old-width' => 'عرض اصلی:', 141 | 'resize-new-height' => 'ارتفاع:', 142 | 'resize-new-width' => 'عرض:', 143 | 144 | 'locale-bootbox' => 'fa', 145 | ]; 146 | -------------------------------------------------------------------------------- /src/Services/ArchiveService.php: -------------------------------------------------------------------------------- 1 | zip = $zip; 30 | $this->request = $request; 31 | $this->pathPrefix = Storage::disk($request->input('disk')) 32 | ->getDriver() 33 | ->getAdapter() 34 | ->getPathPrefix(); 35 | } 36 | 37 | /** 38 | * Create new zip archive 39 | * 40 | * @return array 41 | */ 42 | public function create() 43 | { 44 | 45 | if ($this->createArchive()) { 46 | return [ 47 | 'result' => [ 48 | 'status' => 'success', 49 | 'message' => null, 50 | ], 51 | ]; 52 | } 53 | 54 | return [ 55 | 'result' => [ 56 | 'status' => 'warning', 57 | 'message' => 'zipError', 58 | ], 59 | ]; 60 | } 61 | 62 | /** 63 | * Extract 64 | * 65 | * @return array 66 | */ 67 | public function extract() 68 | { 69 | if ($this->extractArchive()) { 70 | return [ 71 | 'result' => [ 72 | 'status' => 'success', 73 | 'message' => null, 74 | ], 75 | ]; 76 | } 77 | 78 | return [ 79 | 'result' => [ 80 | 'status' => 'warning', 81 | 'message' => 'zipError', 82 | ], 83 | ]; 84 | } 85 | 86 | /** 87 | * Create zip archive 88 | * 89 | * @return bool 90 | */ 91 | protected function createArchive() 92 | { 93 | // elements list 94 | $elements = $this->request->input('elements'); 95 | 96 | // create or overwrite archive 97 | if ( 98 | $this->zip->open( 99 | $this->createName(), 100 | ZIPARCHIVE::OVERWRITE | ZIPARCHIVE::CREATE 101 | ) === true 102 | ) { 103 | // files processing 104 | if ($elements['files']) { 105 | foreach ($elements['files'] as $file) { 106 | $this->zip->addFile( 107 | $this->pathPrefix . $file, 108 | basename($file) 109 | ); 110 | } 111 | } 112 | 113 | // directories processing 114 | if ($elements['directories']) { 115 | $this->addDirs($elements['directories']); 116 | } 117 | 118 | $this->zip->close(); 119 | 120 | return true; 121 | } 122 | 123 | return false; 124 | } 125 | 126 | /** 127 | * Archive extract 128 | * 129 | * @return bool 130 | */ 131 | protected function extractArchive() 132 | { 133 | $zipPath = $this->pathPrefix . $this->request->input('path'); 134 | 135 | $rootPath = dirname($zipPath); 136 | 137 | // extract to new folder 138 | $folder = $this->request->input('folder'); 139 | 140 | if ($this->zip->open($zipPath) === true) { 141 | $this->zip->extractTo($folder ? $rootPath . '/' . $folder : $rootPath); 142 | $this->zip->close(); 143 | 144 | 145 | return true; 146 | } 147 | 148 | return false; 149 | } 150 | 151 | /** 152 | * Add directories - recursive 153 | * 154 | * @param array $directories 155 | */ 156 | protected function addDirs(array $directories) 157 | { 158 | foreach ($directories as $directory) { 159 | 160 | // Create recursive directory iterator 161 | $files = new RecursiveIteratorIterator( 162 | new RecursiveDirectoryIterator($this->pathPrefix . $directory, false), 163 | RecursiveIteratorIterator::LEAVES_ONLY 164 | ); 165 | 166 | foreach ($files as $name => $file) { 167 | // Get real and relative path for current item 168 | $filePath = $file->getRealPath(); 169 | $relativePath = substr( 170 | $filePath, 171 | strlen($this->fullPath($this->request->input('path'))) 172 | ); 173 | 174 | if (!$file->isDir()) { 175 | // Add current file to archive 176 | $this->zip->addFile($filePath, $relativePath); 177 | } else { 178 | // add empty folders 179 | if (!glob($filePath . '/*')) { 180 | $this->zip->addEmptyDir($relativePath); 181 | } 182 | } 183 | } 184 | } 185 | } 186 | 187 | /** 188 | * Create archive name with full path 189 | * 190 | * @return string 191 | */ 192 | protected function createName() 193 | { 194 | return $this->fullPath($this->request->input('path')) 195 | . $this->request->input('name'); 196 | } 197 | 198 | /** 199 | * Generate full path 200 | * 201 | * @param $path 202 | * 203 | * @return string 204 | */ 205 | protected function fullPath($path) 206 | { 207 | return $path ? $this->pathPrefix . $path . '/' : $this->pathPrefix; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/Services/DirectoryService.php: -------------------------------------------------------------------------------- 1 | model = new Directory(); 21 | } 22 | 23 | public function listDirectories(Directory $directory, $recursive = false) 24 | { 25 | if ($recursive) { 26 | $dirs = collect($this->disk->allDirectories($directory->path)); 27 | } 28 | 29 | $dirs = collect($this->disk->directories($directory->path)); 30 | 31 | return $dirs; 32 | } 33 | 34 | public function listFiles(Directory $directory, $recursive = false) 35 | { 36 | if ($recursive) { 37 | $dirs = collect($this->disk->allFiles($directory->path)); 38 | } 39 | 40 | $dirs = collect($this->disk->files($directory->path)); 41 | 42 | return $dirs; 43 | } 44 | 45 | public function createDirectory(array $data) 46 | { 47 | $path = $this->base_directory . $this->ds . $data['name']; 48 | 49 | if (!checkPath($path, $this->disk_name)) { 50 | if ($this->disk->makeDirectory($path)) { 51 | DB::transaction(function () use ($data, $path) { 52 | $this->model->create([ 53 | // 'user_id' => user()->id, 54 | 'name' => $data['name'], 55 | 'description' => $data['description'] ?? '', 56 | 'path' => $path, 57 | 'parent_id' => $data['parent_id'] ?? 0, 58 | 'disk' => $this->disk_name, 59 | ]); 60 | }); 61 | return true; 62 | } else { 63 | 64 | // $this->error('Directory "' . $directory . '" already exists.'); 65 | // $this->error('Can not create directory.'); 66 | return false; 67 | } 68 | } 69 | 70 | return false; 71 | } 72 | 73 | public function renameDirectory(Directory $directory, $newName) 74 | { 75 | if ($directory->name == $newName) return false; 76 | 77 | 78 | $path = $this->base_directory . $this->ds . $directory->name; 79 | 80 | if ($this->disk->exists($path)) { 81 | DB::transaction(function () use ($directory, $newName) { 82 | $directory->update([ 83 | 'name' => $newName 84 | ]); 85 | }); 86 | 87 | if ($this->disk->move($directory->name, $newName)) return true; 88 | }; 89 | return false; 90 | } 91 | 92 | public function deleteDirectory(Directory $directory) 93 | { 94 | $path = $directory->path; 95 | 96 | if (!checkPath($path, $this->disk_name)) { 97 | return false; // directory does not exists 98 | } 99 | 100 | $directoryFiles = array_merge($this->disk->directories($path), $this->disk->files($path)); 101 | if ($directoryFiles) 102 | return false; // directory is not empty 103 | 104 | if ($this->disk->deleteDirectory($path)) { 105 | DB::transaction(function () use ($directory) { 106 | $directory->forceDelete(); 107 | }); 108 | return true; 109 | } 110 | 111 | return false; 112 | } 113 | 114 | /** 115 | * Return files and directories within a directory. 116 | * 117 | * @param string $directory 118 | * 119 | * @return array 120 | */ 121 | public function directoryInfo(Directory $directory) 122 | { 123 | // Get the names of the sub directorys within this directory 124 | $subFolders = collect($this->disk->directories($directory->path))->reduce(function ($subFolders, $subFolder) { 125 | if (!$this->isItemHidden($subFolder)) { 126 | $subFolders[] = $this->directoryDetails($subFolder); 127 | } 128 | 129 | return $subFolders; 130 | }, collect([])); 131 | 132 | // Get all files within this directory 133 | $files = collect($this->disk->files($directory->path))->reduce(function ($files, $path) { 134 | if (!$this->isItemHidden($path)) { 135 | $files[] = $this->fileDetails($path); 136 | } 137 | 138 | return $files; 139 | }, collect([])); 140 | 141 | $itemsCount = $subFolders->count() + $files->count(); 142 | 143 | return compact('directory', 'subFolders', 'files', 'itemsCount'); 144 | } 145 | 146 | /** 147 | * Return an array of directory details for a given directory. 148 | * 149 | * @param $path 150 | * 151 | * @return array 152 | */ 153 | public function directoryDetails($path) 154 | { 155 | $path = '/' . ltrim($path, '/'); 156 | 157 | return [ 158 | 'name' => basename($path), 159 | 'mime_type' => 'directory', 160 | 'disk_path' => $path, 161 | 'modified' => $this->lastModified($path), 162 | ]; 163 | } 164 | 165 | /** 166 | * Return an array of file details for a given file. 167 | * 168 | * @param $path 169 | * 170 | * @return array 171 | */ 172 | public function fileDetails($path) 173 | { 174 | $path = '/' . ltrim($path, '/'); 175 | 176 | return [ 177 | 'original_name' => $this->getOriginalNameFromPath($path), 178 | 'name' => basename($path), 179 | 'disk_path' => $path, 180 | 'url_path' => $this->disk->url($path), 181 | 'extension' => $this->getExtention($path), 182 | 'mime_type' => $this->getMime($path), 183 | 'size' => $this->disk->size($path), 184 | 'human_size' => $this->getHumanReadableSize($this->disk->size($path)), 185 | 'modified' => $this->lastModified($path), 186 | ]; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /config/filemanager.php: -------------------------------------------------------------------------------- 1 | env("FILEMANAGER_BASE_DIR", 'filemanager'), 7 | 8 | /** 9 | * 10 | * route configs that you want to use default work package 11 | * 12 | * The default group settings for the elFinder routes. 13 | * prefix result return => yourdomain.test/API_PREFIX/API_VERSION/FILE_MANAGER_PREFIX/ 14 | * 15 | */ 16 | 'routes' => [ 17 | 'prefix' => env('FILEMANAGER_ROUTE_PREFIX', 'filemanger'), 18 | 'web' => [ 19 | 'middleware' => ['web', 'auth'], //Set to empty to disable middleware filter 20 | ], 21 | 'api' => [ 22 | 'api_prefix' => env('FILEMANAGER_API_PREFIX', 'api'), 23 | 'middleware' => ['api'], //Set to null to disable middleware filter 24 | ], 25 | ], 26 | 27 | 'database' => [ 28 | // for track who work with filemanger (create directory, upload, ...) 29 | 'user_model' => \App\Models\User::class, 30 | 31 | 'files' => [ 32 | 'table' => 'files', 33 | 'model' => \Miladimos\FileManager\Models\File::class, 34 | ], 35 | 'file_group_table' => [ 36 | 'table' => 'file_groups' 37 | ], 38 | 'directories' => [ 39 | 'table' => 'directories', 40 | 'model' => \Miladimos\FileManager\Models\Directory::class, 41 | ], 42 | ], 43 | 44 | /** 45 | * List of disk names that you want to use for upload 46 | * 47 | * local, public 48 | * 49 | */ 50 | 'disk' => env('FILEMANAGER_DISK', 'local'), 51 | 52 | /** 53 | * 54 | * Default Locale 55 | * available locale : en - fa 56 | * for display views 57 | */ 58 | 'locales' => [ 59 | 'default' => env("FILEMANAGER_LOCALE", 'en'), 60 | 61 | 'en' => [ 62 | 'title' => 'English', 63 | 'flag' => '', 64 | 'dir' => 'ltr', 65 | ], 66 | 67 | 'fa' => [ 68 | 'title' => 'فارسی', 69 | 'flag' => '', 70 | 'dir' => 'rtl', 71 | ], 72 | ], 73 | 74 | 'download_link_expire' => '10', // in minute 75 | 76 | /** 77 | * The maximum upload file size of an item in bytes. 78 | * Adding a larger file will result in an exception. 79 | */ 80 | 'max_file_size' => 1024 * 1024 * 10, 81 | 82 | 'max_image_width' => 1024, 83 | 84 | 'max_image_height' => 1024, 85 | 86 | 'image_quality' => 80, 87 | 88 | /** 89 | * strategies 90 | * 91 | * directory path template. 92 | * Variables: 93 | * - `Y` Year, example: 2021 94 | * - `m` Month, example: 04 95 | * - `d` Date, example: 08 96 | * - `H` Hour, example: 12 97 | * - `i` Minute, example: 15 98 | * 99 | * sizes in pixel 100 | */ 101 | 'strategies' => [ 102 | 'file' => [ 103 | 'path' => 'files/{Y}/{m}/{d}/timestamp-$originalFilename', 104 | "date_time_prefix" => true, // before name : time_name.ext 105 | 'max_size' => '2m', 106 | ], 107 | 'thumbnail' => [ 108 | 'path' => 'thumbnails/{Y}/{m}/{d}/timestamp-$originalFilename', 109 | "date_time_prefix" => false, // before name : time_name.ext 110 | 'height' => 60, 111 | 'width' => 60, 112 | 'fit' => 'stretch', // ['stretch', 'crop', 'contain', 'max', 'fill'] 113 | 'max_size' => '2m', 114 | ], 115 | 'avatar' => [ 116 | 'path' => 'thumbnails/{Y}/{m}/{d}/timestamp-$originalFilename', 117 | "date_time_prefix" => false, // before name : time_name.ext 118 | 'height' => 250, 119 | 'width' => 250, 120 | 'fit' => 'stretch', // ['stretch', 'crop', 'contain', 'max', 'fill'] 121 | "sizes" => ["16", "24", "32", "64", "128", "320"], 122 | "thumb" => "320", 123 | 'max_size' => '2m', 124 | ], 125 | ], 126 | 127 | // for uploads 128 | 'allowed_mimes' => [ 129 | 'image/gif', 130 | 'image/jpeg', 131 | 'image/png', 132 | 'image/bmp', 133 | 'image/png', 134 | 'image/tiff', 135 | 'application/json', 136 | 'application/x-tar', 137 | 'application/zip', 138 | ], 139 | 140 | // for uploads 141 | 'allowed_extensions' => [ 142 | 'jpeg', 'jpg', 'png', 'gif', 'webp', 'docx', 143 | 'pdf', 'ttf', 'css', 'php', 'html', 'htm', 'js', 144 | 'xls', 'txt', 'xlsx', 'docx', 'pdf', 'rar', 'zip', 145 | 'mp4', 'mp3', 'csv', 'cv', 'tar', 'bz2', 146 | ], 147 | 148 | // for uploads 149 | 'disallow_extensions' => ['exe', 'asm', 'bin', 'o', 'jar'], 150 | 151 | /** 152 | * date() format for file modifications date 153 | * for created_at , updated_at, modified_at 154 | * Doc - https://www.php.net/manual/en/function.date.php 155 | * ex: d.m.y H:i 156 | * and you can use "ago" for ago human readable time ex: 10 minutes ago 157 | */ 158 | 'datetime_format' => 'ago', 159 | 160 | /** 161 | * Show / Hide system files and folders 162 | */ 163 | 'hiddenFiles' => false, 164 | 165 | // in views 166 | 'hide_files_extension' => false, 167 | 168 | 'pagination' => [ 169 | 'folders' => 12, //2 rows 170 | 171 | 'files' => 15, //3 rows 172 | ], 173 | 174 | 'logger' => [ 175 | 'active' => false, 176 | 'channel' => env("LOG_CHANNEL", "stack"), 177 | 'level' => 'info', // default level 178 | ], 179 | 180 | /** 181 | * Image cache ( Intervention Image Cache ) 182 | * 183 | * set 0 - if you don't need cache (default) 184 | * if you want use cache - set the number of minutes for which the value should be cached 185 | */ 186 | 'cache' => 0, 187 | 188 | /*************************************************************************** 189 | * ACL rules list - used for default ACL repository (ConfigACLRepository) 190 | * 191 | * 1 it's user ID 192 | * null - for not authenticated user 193 | * 194 | * 'disk' => 'disk-name' 195 | * 196 | * 'path' => 'folder-name' 197 | * 'path' => 'folder1*' - select folder1, folder12, folder1/sub-folder, ... 198 | * 'path' => 'folder2/*' - select folder2/sub-folder,... but not select folder2 !!! 199 | * 'path' => 'folder-name/file-name.jpg' 200 | * 'path' => 'folder-name/*.jpg' 201 | * 202 | * * - wildcard 203 | * 204 | * access: 0 - deny, 1 - read, 2 - read/write 205 | */ 206 | 'aclRules' => [ 207 | null => [ 208 | //['disk' => 'public', 'path' => '/', 'access' => 2], 209 | ], 210 | 1 => [ 211 | //['disk' => 'public', 'path' => 'images/arch*.jpg', 'access' => 2], 212 | //['disk' => 'public', 'path' => 'files/*', 'access' => 1], 213 | ], 214 | ], 215 | 216 | ]; 217 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Starts](https://img.shields.io/github/stars/miladimos/laravel-filemanager?style=flat&logo=github)](https://github.com/miladimos/laravel-filemanager/forks) 2 | [![Forks](https://img.shields.io/github/forks/miladimos/laravel-filemanager?style=flat&logo=github)](https://github.com/miladimos/laravel-filemanager/stargazers) 3 | 4 | [comment]: <> (- [English](README-en.md)) 5 | 6 | # Under Development 7 | 8 | ##### help us for development :) 9 | 10 | ### for installation in root of your project do these steps: 11 | 12 | ``` php 13 | composer require miladimos/laravel-filemanager 14 | ``` 15 | 16 | 2. Open your config/app.php and add the following lines: 17 | 18 | ```php 19 | // in providers 20 | Miladimos\FileManager\Providers\FileManagerServiceProvider::class, 21 | 22 | // in aliases 23 | Miladimos\FileManager\Facades\FileManagerFacade::class, 24 | ``` 25 | 26 | 3. Run the command below to install package: 27 | 28 | ``` 29 | php artisan filemanager:install 30 | ``` 31 | 32 | ### Configuration ! important ! 33 | 34 | next go to the file 35 | 36 | ```php 37 | config/filemanager.php; 38 | ``` 39 | 40 | for initialize file manager first set these confings: 41 | 42 | 1. set default storage to upload file (default is: local) 43 | 2. set base directory name for file manager (default is: filemanager/) 44 | 45 | and run bellow command for initialize: 46 | 47 | ``` php 48 | php artisan filemanager:init 49 | ``` 50 | 51 | then create tables: 52 | 53 | ``` php 54 | php artisan migrate 55 | ``` 56 | 57 | just it :) 58 | 59 | if you are set public disk run bellow command: 60 | 61 | ```php 62 | php artisan storage:link 63 | ``` 64 | 65 | and if you want use ftp add these config in your config/filesystems.php: 66 | 67 | ```php 68 | 'ftp' => [ 69 | 'driver' => 'ftp', 70 | 'host' => 'ftp.example.com', 71 | 'username' => 'your-username', 72 | 'password' => 'your-password', 73 | 74 | // Optional FTP Settings... 75 | // 'port' => 21, 76 | // 'root' => '', 77 | // 'passive' => true, 78 | // 'ssl' => true, 79 | // 'timeout' => 30, 80 | ], 81 | ``` 82 | 83 | and for sftp use this: 84 | 85 | ```php 86 | 'sftp' => [ 87 | 'driver' => 'sftp', 88 | 'host' => 'example.com', 89 | 'username' => 'your-username', 90 | 'password' => 'your-password', 91 | 92 | // Settings for SSH key based authentication... 93 | 'privateKey' => '/path/to/privateKey', 94 | 'password' => 'encryption-password', 95 | 96 | // Optional SFTP Settings... 97 | // 'port' => 22, 98 | // 'root' => '', 99 | // 'timeout' => 30, 100 | ], 101 | ``` 102 | 103 | [comment]: <> (### نحوه استفاده) 104 | 105 | [comment]: <> (برای اپلود فایل:) 106 | 107 | [comment]: <> (```) 108 | 109 | [comment]: <> (public function store(Request $request)) 110 | 111 | [comment]: <> ({ ) 112 | 113 | [comment]: <> ( // This will upload your file to the default folder of selected in config storage) 114 | 115 | [comment]: <> ( UploadService::uploadFile($request->file('some_file'));) 116 | 117 | [comment]: <> ( // This will upload your file to the given as second parameter path of default storage) 118 | 119 | [comment]: <> ( UploadService::uploadFile($request->file('some_file'), 'path/to/upload');) 120 | 121 | [comment]: <> ( // This will upload your file to the given storage) 122 | 123 | [comment]: <> ( UploadService::uploadFile($request->file('some_file'), 'path/to/upload', 'storage_name');) 124 | 125 | [comment]: <> ( // This will also resize image to the given width and height) 126 | 127 | [comment]: <> ( UploadService::uploadFile($request->file('some_file'), 'path/to/upload', 'storage_name');) 128 | 129 | [comment]: <> (}) 130 | 131 | [comment]: <> (```) 132 | 133 | [comment]: <> (برای آپلود عکس با فرمت base64:) 134 | 135 | [comment]: <> (```php) 136 | 137 | [comment]: <> (public function store(Request $request)) 138 | 139 | [comment]: <> ({ ) 140 | 141 | [comment]: <> ( // This will upload your file to the default folder of selected in config storage) 142 | 143 | [comment]: <> ( UploadService::uploadBase64Image($request->input('image'));) 144 | 145 | [comment]: <> ( // This will upload your file to the given as second parameter path of default storage) 146 | 147 | [comment]: <> ( UploadService::uploadFile($request->input('image'), 'path/to/upload');) 148 | 149 | [comment]: <> ( // This will upload your file to the given storage) 150 | 151 | [comment]: <> ( UploadService::uploadFile($request->input('image'), 'path/to/upload', 'storage_name');) 152 | 153 | [comment]: <> ( // This will also resize image to the given width and height) 154 | 155 | [comment]: <> ( UploadService::uploadFile($request->input('image'), 'path/to/upload', 'storage_name');) 156 | 157 | [comment]: <> (}) 158 | 159 | [comment]: <> (```) 160 | 161 | ### Features ❤️ 162 | 163 | #### You are free to use whatever you like 😎 ( you can just use services in your coding or use apis for your graphical file manager or whatever ...) 164 | 165 | ### Backend Services: 166 | 167 | ##### Directory service: 168 | 169 | ```php 170 | use Miladimos\FileManager\Services\DirectoryService; 171 | 172 | $service = new DirectoryService(); 173 | $service->createDirectory($name); // name of directory for create 174 | $service->deleteDirectory($uuid); // uuid of directory for delete in db and disk 175 | $service->listDirectories($path) // list all directories in given path 176 | $service->listDirectoriesRecursive($path); // list all directories in given path Recursively 177 | ``` 178 | 179 | ##### File service: 180 | 181 | ```php 182 | use Miladimos\FileManager\Services\FileService; 183 | 184 | $service = new FileService(); // or resolve(FileService::class) 185 | ``` 186 | 187 | ##### FileGroup service: 188 | 189 | ```php 190 | use Miladimos\FileManager\Services\FileGroupService; 191 | 192 | $service = new FileGroupService(); 193 | $service->allFileGroups(); 194 | $service->createFileGroup(array $data); // $data = ['title', 'description'] 195 | $service->updateFileGroup(FileGroup $fileGroup, array $data); // $data = ['title', 'description'] 196 | $service->deleteFileGroup(FileGroup $fileGroup); 197 | ``` 198 | 199 | ##### Image service: 200 | 201 | ```php 202 | use Miladimos\FileManager\Services\ImageService; 203 | 204 | $service = new ImageService(); 205 | ``` 206 | 207 | ##### Upload service: 208 | 209 | ```php 210 | use Miladimos\FileManager\Services\UploadService; 211 | 212 | $service = new UploadService(); 213 | ``` 214 | 215 | ### API over backend services: 216 | 217 | for all requests set these headers: 218 | 219 | Content-Type : application/x-www-form-urlencoded 220 | 221 | ``` 222 | prefix = /api_prefix/filemanager_api_version/route_prefix 223 | 224 | // Directories 225 | POST -> prefix/directories // store new directory 226 | DELETE -> prefix/directories // receive directories field: it can be array of uuid or one uuid of directories for delete 227 | 228 | 229 | // File Groups 230 | GET -> prefix/filegroups // return all available file groups 231 | POST -> prefix/filegroups // store new file groups -> receive : title, description 232 | PUT -> prefix/filegroups/{filegroup}/update // update file groups -> receive : title, description 233 | DELETE -> prefix/filegroups/{filegroup} // delete file groups 234 | ``` 235 | 236 | ### BACKEND TODO: 237 | 238 | - [x] Directory service - list, list recursive, create, delete, move 239 | - [ ] File service - list, delete, move 240 | - [ ] Upload service - 241 | - [ ] Image service - 242 | - [ ] FileGroup service - 243 | - [ ] Archive service - zip, tar 244 | 245 | ### FRONTEND TODO: 246 | 247 | - [ ] Web view - 248 | -------------------------------------------------------------------------------- /src/Services/Service.php: -------------------------------------------------------------------------------- 1 | disk = Storage::disk(config('filemanager.disk')); 35 | $this->disk_name = config('filemanager.disk'); 36 | $this->base_directory = config('filemanager.base_directory'); 37 | $this->mimeDetect = new FinfoMimeTypeDetector(); 38 | $this->loggerService = new LoggerService(); 39 | } 40 | 41 | /** 42 | * Return the last modified time. If a timestamp can not be found fall back 43 | * to today's date and time... 44 | * 45 | * @param string $path 46 | * 47 | * @return Carbon 48 | */ 49 | public function lastModified(string $path) 50 | { 51 | try { 52 | return Carbon::createFromTimestamp($this->disk->lastModified($path)); 53 | } catch (\Exception $e) { 54 | return Carbon::now(); 55 | } 56 | } 57 | 58 | /** 59 | * check storage path 60 | * 61 | * @param $src 62 | * 63 | * @return string 64 | */ 65 | protected function getStorageFolder($src) 66 | { 67 | if ($this->disk_name == "storage") 68 | return storage_path($src); 69 | if ($this->disk_name == "public") 70 | return public_path($src); 71 | return public_path($src); 72 | } 73 | 74 | /** 75 | * Sanitize the directory name. 76 | * 77 | * @param $directory 78 | * 79 | * @return mixed 80 | */ 81 | protected function cleanDirectoryName($directory) 82 | { 83 | return DIRECTORY_SEPARATOR . trim(str_replace('..', '', $directory), DIRECTORY_SEPARATOR); 84 | } 85 | 86 | /** 87 | * generate and unique & random name 88 | * 89 | * @param int $length 90 | * @return string 91 | */ 92 | protected function generateRandomFileName(int $length = 10): string 93 | { 94 | do { 95 | $randomName = Str::random($length); 96 | $check = File::query() 97 | ->where("name", $randomName) 98 | ->first(); 99 | } while (!empty($check)); 100 | 101 | return $randomName; 102 | } 103 | 104 | /** 105 | * generate and unique & random name 106 | * 107 | * @param int $length 108 | * @return string 109 | */ 110 | protected function generateRandomName(int $length = 10): string 111 | { 112 | $chars = range('a', 'z'); 113 | $charsC = range('A', 'Z'); 114 | $nums = range(1, 9) + 1; 115 | 116 | $merged = implode("", array_merge($chars, $charsC, $nums)); 117 | $str = str_shuffle($merged); 118 | $randomName = Str::random(3) . substr($str, 0, $length); 119 | 120 | return $randomName; 121 | } 122 | 123 | protected function getHumanReadableSize(int $sizeInBytes): string 124 | { 125 | $units = ['B', 'KB', 'MB', 'GB', 'TB']; 126 | 127 | if ($sizeInBytes == 0) { 128 | return '0 ' . $units[1]; 129 | } 130 | 131 | for ($i = 0; $sizeInBytes > 1024; $i++) { 132 | $sizeInBytes /= 1024; 133 | } 134 | 135 | return round($sizeInBytes, 2) . ' ' . $units[$i]; 136 | } 137 | 138 | /** 139 | * Get content for the selected disk and path 140 | * 141 | * @param $disk 142 | * @param null $path 143 | * 144 | * @return array 145 | */ 146 | protected function getContent($disk, $path = null) 147 | { 148 | $content = Storage::disk($disk)->listContents($path); 149 | 150 | // get a list of directories 151 | $directories = $this->filterDir($disk, $content); 152 | 153 | // get a list of files 154 | $files = $this->filterFile($disk, $content); 155 | 156 | return compact('directories', 'files'); 157 | } 158 | 159 | /** 160 | * Get only directories 161 | * 162 | * @param $content 163 | * 164 | * @return array 165 | */ 166 | protected function filterDir($disk, $content) 167 | { 168 | // select only dir 169 | $dirsList = Arr::where($content, function ($item) { 170 | return $item['type'] === 'dir'; 171 | }); 172 | 173 | // remove 'filename' param 174 | $dirs = array_map(function ($item) { 175 | return Arr::except($item, ['filename']); 176 | }, $dirsList); 177 | 178 | return array_values($dirs); 179 | } 180 | 181 | /** 182 | * Get only files 183 | * 184 | * @param $disk 185 | * @param $content 186 | * 187 | * @return array 188 | */ 189 | protected function filterFile($disk, $content) 190 | { 191 | // select only files 192 | $files = Arr::where($content, function ($item) { 193 | return $item['type'] === 'file'; 194 | }); 195 | 196 | return array_values($files); 197 | } 198 | 199 | /** 200 | * Get directories for tree module 201 | * 202 | * @param $disk 203 | * @param $path 204 | * 205 | * @return array 206 | */ 207 | protected function getDirectoriesTree($disk, $path = null) 208 | { 209 | $directories = $this->directoriesWithProperties($disk, $path); 210 | 211 | foreach ($directories as $index => $dir) { 212 | $directories[$index]['props'] = [ 213 | 'hasSubdirectories' => Storage::disk($disk) 214 | ->directories($dir['path']) ? true : false, 215 | ]; 216 | } 217 | 218 | return $directories; 219 | } 220 | 221 | /** 222 | * File properties 223 | * 224 | * @param $disk 225 | * @param null $path 226 | * 227 | * @return mixed 228 | */ 229 | protected function fileProperties($disk, $path = null) 230 | { 231 | $file = $this->disk->getMetadata($path); 232 | 233 | $pathInfo = pathinfo($path); 234 | 235 | $file['basename'] = $pathInfo['basename']; 236 | $file['dirname'] = $pathInfo['dirname'] === '.' ? '' 237 | : $pathInfo['dirname']; 238 | $file['extension'] = isset($pathInfo['extension']) 239 | ? $pathInfo['extension'] : ''; 240 | $file['filename'] = $pathInfo['filename']; 241 | 242 | return $file; 243 | } 244 | 245 | /** 246 | * Work out if an item (file or folder) is hidden (begins with a "."). 247 | * 248 | * @param $item 249 | * 250 | * @return bool 251 | */ 252 | protected function isItemHidden($item) 253 | { 254 | return Str::startsWith(last(explode(DIRECTORY_SEPARATOR, $item)), '.'); 255 | } 256 | 257 | public function generatePath($path) 258 | { 259 | return $path . DIRECTORY_SEPARATOR; 260 | } 261 | 262 | public function getMime($path) 263 | { 264 | $path = $this->disk->path($path); 265 | return $this->mimeDetect->detectMimeTypeFromFile($path) ?? 'unknown/type'; 266 | } 267 | 268 | public function getExtention($path) 269 | { 270 | $path = $this->disk->path($path); 271 | return pathinfo($path)['extension'] ?? 'unknown'; 272 | } 273 | 274 | public function getOriginalNameFromPath($path) 275 | { 276 | return substr(basename($path), strpos(basename($path), "-") + 1); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/Services/UploadService.php: -------------------------------------------------------------------------------- 1 | directoryService = new DirectoryService(); 30 | 31 | $this->fileModel = new File(); 32 | 33 | $this->directoryModel = new Directory(); 34 | } 35 | 36 | public function uploadFile(UploadedFile $uploadedFile, $directory_id = 0) 37 | { 38 | /// ProcessUpload::dispatch($upload, $key); 39 | 40 | $path = $this->directoryModel->find($directory_id)->path; 41 | 42 | if ($uploadedFile->isValid() && $this->fileExtIsAllowed($uploadedFile->getExtension())) { 43 | 44 | $year = Carbon::now()->year; 45 | $month = Carbon::now()->month; 46 | $day = Carbon::now()->day; 47 | 48 | $originalName = $uploadedFile->getClientOriginalName(); 49 | $fileExt = $uploadedFile->getClientOriginalExtension(); 50 | $mimeType = $uploadedFile->getClientMimeType(); 51 | $fileSize = $uploadedFile->getSize(); // in bytes 52 | 53 | // $uploadPath = "{$path}{$this->ds}{$year}{$this->ds}{$month}{$this->ds}{$day}"; 54 | 55 | // $this->mkdir_directory_if_not_exists($uploadPath); 56 | 57 | $finalFileName = Carbon::now()->timestamp . "-{$originalName}"; 58 | 59 | $fullUploadedPath = $path . $this->ds . $finalFileName; 60 | 61 | if ($this->disk->put($fullUploadedPath, $uploadedFile->getContent())) { 62 | DB::transaction(function () use ($originalName, $finalFileName, $directory_id, $path, $fullUploadedPath, $fileSize, $mimeType, $fileExt) { 63 | $this->fileModel->create([ 64 | 'original_name' => $originalName, 65 | 'name' => $finalFileName, 66 | 'disk' => $this->disk_name, 67 | 'directory_id' => $directory_id, 68 | // 'user_id' => user()->id, 69 | 'path' => $path, 70 | 'url' => url('storage/' . $fullUploadedPath), 71 | 'size' => $fileSize, 72 | 'mime_type' => $mimeType, 73 | 'extension' => $fileExt, 74 | ]); 75 | }); 76 | 77 | return true; 78 | } else { 79 | return false; 80 | } 81 | } 82 | 83 | return false; 84 | } 85 | 86 | public function uploadImage(UploadedFile $uploadedFile, $directory_id = 0) 87 | { 88 | $path = $this->directoryModel->find($directory_id)->path; 89 | 90 | if ($uploadedFile->isValid() && $this->fileExtIsAllowed($uploadedFile->getClientOriginalExtension())) { 91 | 92 | $image = Image::make($uploadedFile->getRealPath()); 93 | $year = Carbon::now()->year; 94 | $month = Carbon::now()->month; 95 | $day = Carbon::now()->day; 96 | 97 | $originalName = $uploadedFile->getClientOriginalName(); 98 | $fileExt = $uploadedFile->getClientOriginalExtension(); 99 | $mimeType = $uploadedFile->getClientMimeType(); 100 | $fileSize = $uploadedFile->getSize(); 101 | 102 | $uploadPath = "{$path}{$this->ds}{$year}{$this->ds}{$month}{$this->ds}{$day}"; 103 | 104 | // $uploadPath = "{$path}{$this->ds}{$year}{$this->ds}{$month}{$this->ds}{$day}"; 105 | 106 | // $this->mkdir_directory_if_not_exists($uploadPath); 107 | 108 | $finalFileName = Carbon::now()->timestamp . "-{$originalName}"; 109 | 110 | $fullUploadedPath = $path . $this->ds . $finalFileName; 111 | 112 | event(new BeforeUpload()); 113 | 114 | if ($this->disk->put($fullUploadedPath, $image->encode())) { 115 | DB::transaction(function () use ($originalName, $finalFileName, $directory_id, $path, $fullUploadedPath, $fileSize, $mimeType, $fileExt, $image) { 116 | $this->fileModel->create([ 117 | 'original_name' => $originalName, 118 | 'name' => $finalFileName, 119 | 'disk' => $this->disk_name, 120 | 'directory_id' => $directory_id, 121 | // 'user_id' => user()->id, 122 | 'path' => $path, 123 | 'url' => url('storage/' . $fullUploadedPath), 124 | 'size' => $fileSize, 125 | 'mime_type' => $mimeType, 126 | 'extension' => $fileExt, 127 | 'width' => $image->width(), 128 | 'height' => $image->height(), 129 | ]); 130 | }); 131 | 132 | event(new AfterUpload()); 133 | 134 | return true; 135 | } else { 136 | return false; 137 | } 138 | } 139 | 140 | return false; 141 | } 142 | 143 | function mkdir_directory_if_not_exists($dirPath) 144 | { 145 | dd(explode("/", $dirPath)); 146 | if (!checkPath($dirPath, $this->disk_name)) { 147 | $this->directoryService->createDirectory([]); 148 | } 149 | } 150 | 151 | // file extension is allowed from config 152 | public function fileExtIsAllowed($ext) 153 | { 154 | $exts = config('filemanager.allowed_extensions'); 155 | return in_array($ext, $exts); 156 | } 157 | 158 | // file mime is allowed from config 159 | public function fileMimeIsAllowed($mime) 160 | { 161 | $mimes = config('filemanager.allowed_mimes'); 162 | return in_array($mime, $mimes); 163 | } 164 | 165 | public function saveUploadedFiles(UploadedFile $files, $directory_id = 0) 166 | { 167 | $path = $this->directoryModel->find($directory_id)->path; 168 | 169 | return $files->getUploadedFiles()->reduce(function ($uploaded, UploadedFile $file) use ($path) { 170 | $fileName = $file->getClientOriginalName(); 171 | if ($this->disk->exists($path . $fileName)) { 172 | // $this->errors[] = 'File ' . $path . $fileName . ' already exists in this folder.'; 173 | 174 | return $uploaded; 175 | } 176 | 177 | if (!$file->storeAs($path, $fileName, [ 178 | 'disk' => $this->diskName, 179 | 'visibility' => $this->access, 180 | ])) { 181 | // $this->errors[] = trans('media-manager::messages.upload_error', ['entity' => $fileName]); 182 | 183 | return $uploaded; 184 | } 185 | $uploaded++; 186 | 187 | return $uploaded; 188 | }, 0); 189 | } 190 | 191 | // public function uploadFileByUrl(string $url, string $field, $fileName = null) 192 | // { 193 | // $uuid = Str::uuid(); 194 | // $file = file_get_contents($url); 195 | // $url = strtok($url, '?'); 196 | // $config = config('upload.files.' . $field); 197 | // 198 | // $orignalName = str_replace('_', '-', pathinfo($url, PATHINFO_FILENAME)); 199 | // $orignalName = str_replace(' ', '-', pathinfo($url, PATHINFO_FILENAME)); 200 | // $extension = pathinfo($url, PATHINFO_EXTENSION); 201 | // $extension = ($extension) ? "." . $extension : $extension; 202 | // $storagePath = $this->disk->getDriver()->getAdapter()->getPathPrefix(); 203 | // 204 | // if ($fileName) { 205 | // $fileNameWithExtension = $fileName . $extension; 206 | // $orignalName = $fileName; 207 | // } else { 208 | // $fileNameWithExtension = $orignalName . $extension; 209 | // } 210 | // 211 | // $this->disk->put('/uploads/' . $uuid . '/' . $fileNameWithExtension, $file); 212 | // 213 | // $mimeType = mime_content_type($storagePath . '/uploads/' . $uuid . '/' . $fileNameWithExtension); 214 | // $mimeFileType = $this->getFileType($mimeType); 215 | // 216 | // $file = $this->fileModel->create([ 217 | // 'private' => array_get($config, 'private', false), 218 | // 'title' => $orignalName, 219 | // 'file_field' => $field, 220 | // 'file_name' => $fileNameWithExtension, 221 | // 'mime_type' => $mimeType, 222 | // 'file_type' => $mimeFileType, 223 | // 'size' => (filesize($storagePath . '/uploads/' . $uuid . '/' . $fileNameWithExtension) / 1024) / 1024, 224 | // 'uuid' => $uuid, 225 | // ]); 226 | // 227 | // 228 | // if ($mimeFileType != 'image' || !array_get($config, 'resize')) { 229 | // return true; 230 | // } 231 | // 232 | // foreach ($config['resize'] as $key => $value) { 233 | // if (array_get($value, 'create_on_upload', false)) { 234 | // $this->resizeImage($file, $key); 235 | // continue; 236 | // } 237 | // 238 | // ProcessUpload::dispatch($file, $key); 239 | // } 240 | // 241 | // return $file; 242 | // } 243 | } 244 | -------------------------------------------------------------------------------- /src/Models/File.php: -------------------------------------------------------------------------------- 1 | 'int', 25 | ]; 26 | 27 | public function user() 28 | { 29 | return $this->belongsTo(config('filemanager.database.user_model'), 'user_id'); 30 | } 31 | 32 | public function groups() 33 | { 34 | return $this->belongsToMany(FileGroup::class, 'file_group_pivot'); 35 | } 36 | 37 | public function fileable() 38 | { 39 | return $this->morphTo(); 40 | } 41 | 42 | public function getFullUrl(string $conversionName = ''): string 43 | { 44 | return url($this->getUrl($conversionName)); 45 | } 46 | 47 | public function getExtensionAttribute(): string 48 | { 49 | return pathinfo($this->file_name, PATHINFO_EXTENSION); 50 | } 51 | 52 | public function getHumanReadableSizeAttribute(): string 53 | { 54 | return File::getHumanReadableSize($this->size); 55 | } 56 | 57 | public function getDiskDriverName(): string 58 | { 59 | return strtolower(config("filesystems.disks.{$this->disk}.driver")); 60 | } 61 | 62 | public function getConversionsDiskDriverName(): string 63 | { 64 | $diskName = $this->conversions_disk ?? $this->disk; 65 | 66 | return strtolower(config("filesystems.disks.{$diskName}.driver")); 67 | } 68 | 69 | public function hasCustomProperty(string $propertyName): bool 70 | { 71 | return Arr::has($this->custom_properties, $propertyName); 72 | } 73 | 74 | /** 75 | * Get the value of custom property with the given name. 76 | * 77 | * @param string $propertyName 78 | * @param mixed $default 79 | * 80 | * @return mixed 81 | */ 82 | public function getCustomProperty(string $propertyName, $default = null) 83 | { 84 | return Arr::get($this->custom_properties, $propertyName, $default); 85 | } 86 | 87 | /** 88 | * @param string $name 89 | * @param mixed $value 90 | * 91 | * @return $this 92 | */ 93 | public function setCustomProperty(string $name, $value): self 94 | { 95 | $customProperties = $this->custom_properties; 96 | 97 | Arr::set($customProperties, $name, $value); 98 | 99 | $this->custom_properties = $customProperties; 100 | 101 | return $this; 102 | } 103 | 104 | public function forgetCustomProperty(string $name): self 105 | { 106 | $customProperties = $this->custom_properties; 107 | 108 | Arr::forget($customProperties, $name); 109 | 110 | $this->custom_properties = $customProperties; 111 | 112 | return $this; 113 | } 114 | 115 | public function getMediaConversionNames(): array 116 | { 117 | $conversions = ConversionCollection::createForMedia($this); 118 | 119 | return $conversions->map(fn (Conversion $conversion) => $conversion->getName())->toArray(); 120 | } 121 | 122 | public function getGeneratedConversions(): Collection 123 | { 124 | return collect($this->generated_conversions ?? []); 125 | } 126 | 127 | 128 | public function markAsConversionGenerated(string $conversionName): self 129 | { 130 | $generatedConversions = $this->generated_conversions; 131 | 132 | Arr::set($generatedConversions, $conversionName, true); 133 | 134 | $this->generated_conversions = $generatedConversions; 135 | 136 | $this->save(); 137 | 138 | return $this; 139 | } 140 | 141 | public function markAsConversionNotGenerated(string $conversionName): self 142 | { 143 | $generatedConversions = $this->generated_conversions; 144 | 145 | Arr::set($generatedConversions, $conversionName, false); 146 | 147 | $this->generated_conversions = $generatedConversions; 148 | 149 | $this->save(); 150 | 151 | return $this; 152 | } 153 | 154 | public function hasGeneratedConversion(string $conversionName): bool 155 | { 156 | $generatedConversions = $this->getGeneratedConversions(); 157 | 158 | return $generatedConversions[$conversionName] ?? false; 159 | } 160 | 161 | public function toResponse($request) 162 | { 163 | return $this->buildResponse($request, 'attachment'); 164 | } 165 | 166 | public function toInlineResponse($request) 167 | { 168 | return $this->buildResponse($request, 'inline'); 169 | } 170 | 171 | private function buildResponse($request, string $contentDispositionType) 172 | { 173 | $downloadHeaders = [ 174 | 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', 175 | 'Content-Type' => $this->mime_type, 176 | 'Content-Length' => $this->size, 177 | 'Content-Disposition' => $contentDispositionType . '; filename="' . $this->file_name . '"', 178 | 'Pragma' => 'public', 179 | ]; 180 | 181 | return response()->stream(function () { 182 | $stream = $this->stream(); 183 | 184 | fpassthru($stream); 185 | 186 | if (is_resource($stream)) { 187 | fclose($stream); 188 | } 189 | }, 200, $downloadHeaders); 190 | } 191 | 192 | public function getResponsiveImageUrls(string $conversionName = ''): array 193 | { 194 | return $this->responsiveImages($conversionName)->getUrls(); 195 | } 196 | 197 | public function hasResponsiveImages(string $conversionName = ''): bool 198 | { 199 | return count($this->getResponsiveImageUrls($conversionName)) > 0; 200 | } 201 | 202 | public function getSrcset(string $conversionName = ''): string 203 | { 204 | return $this->responsiveImages($conversionName)->getSrcset(); 205 | } 206 | 207 | public function getPreviewUrlAttribute() 208 | { 209 | return $this->hasGeneratedConversion('preview') ? $this->getUrl('preview') : ''; 210 | } 211 | 212 | public function getOriginalUrlAttribute() 213 | { 214 | return $this->getUrl(); 215 | } 216 | 217 | public function move(HasMedia $model, $collectionName = 'default', string $diskName = '', string $fileName = ''): self 218 | { 219 | $newMedia = $this->copy($model, $collectionName, $diskName, $fileName); 220 | 221 | $this->delete(); 222 | 223 | return $newMedia; 224 | } 225 | 226 | public function copy(HasMedia $model, $collectionName = 'default', string $diskName = '', string $fileName = ''): self 227 | { 228 | $temporaryDirectory = TemporaryDirectory::create(); 229 | 230 | $temporaryFile = $temporaryDirectory->path('/') . DIRECTORY_SEPARATOR . $this->file_name; 231 | 232 | /** @var \Spatie\MediaLibrary\MediaCollections\Filesystem $filesystem */ 233 | $filesystem = app(Filesystem::class); 234 | 235 | $filesystem->copyFromMediaLibrary($this, $temporaryFile); 236 | 237 | $fileAdder = $model 238 | ->addMedia($temporaryFile) 239 | ->usingName($this->name) 240 | ->setOrder($this->order_column) 241 | ->withCustomProperties($this->custom_properties); 242 | 243 | if ($fileName !== '') { 244 | $fileAdder->usingFileName($fileName); 245 | } 246 | 247 | $newMedia = $fileAdder 248 | ->toMediaCollection($collectionName, $diskName); 249 | 250 | $temporaryDirectory->delete(); 251 | 252 | return $newMedia; 253 | } 254 | 255 | public function responsiveImages(string $conversionName = ''): RegisteredResponsiveImages 256 | { 257 | return new RegisteredResponsiveImages($this, $conversionName); 258 | } 259 | 260 | public function stream() 261 | { 262 | /** @var \Spatie\MediaLibrary\MediaCollections\Filesystem $filesystem */ 263 | $filesystem = app(Filesystem::class); 264 | 265 | return $filesystem->getStream($this); 266 | } 267 | 268 | public function toHtml() 269 | { 270 | return $this->img()->toHtml(); 271 | } 272 | 273 | public function img(string $conversionName = '', $extraAttributes = []): HtmlableMedia 274 | { 275 | return (new HtmlableMedia($this)) 276 | ->conversion($conversionName) 277 | ->attributes($extraAttributes); 278 | } 279 | 280 | public function __invoke(...$arguments): HtmlableMedia 281 | { 282 | return $this->img(...$arguments); 283 | } 284 | 285 | public function temporaryUpload(): BelongsTo 286 | { 287 | MediaLibraryPro::ensureInstalled(); 288 | 289 | return $this->belongsTo(TemporaryUpload::class); 290 | } 291 | 292 | public static function findWithTemporaryUploadInCurrentSession(array $uuids) 293 | { 294 | MediaLibraryPro::ensureInstalled(); 295 | 296 | return static::query() 297 | ->whereIn('uuid', $uuids) 298 | ->whereHasMorph( 299 | 'model', 300 | [TemporaryUpload::class], 301 | fn (Builder $builder) => $builder->where('session_id', session()->getId()) 302 | ) 303 | ->get(); 304 | } 305 | 306 | 307 | public function setNameAttribute($value) 308 | { 309 | $this->attributes['name'] = now() . '-' . $value; 310 | } 311 | 312 | public function getCreatedAtAttribute($value) 313 | { 314 | return Carbon::parse($value)->diffForHumans(); 315 | } 316 | 317 | public function getUpdatedAtAttribute($value) 318 | { 319 | return Carbon::parse($value)->diffForHumans(); 320 | } 321 | 322 | /** 323 | * generate the link for download file 324 | * this link has expire time 325 | * 326 | * @return string 327 | */ 328 | public function generateDownloadLink() 329 | { 330 | $secret = env('APP_KEY'); 331 | 332 | $expireTime = (int)config('filemanager.download_link_expire'); 333 | 334 | $timestamp = Carbon::now()->addMinutes($expireTime)->timestamp; 335 | $hash = Hash::make($secret . $this->uuid . getUserIP() . $timestamp); 336 | 337 | // return "/api/filemanager/download/$this->uuid?mac=$hash&t=$timestamp"; 338 | return route('filemanager.download', [$this, $hash, $timestamp]); 339 | } 340 | 341 | public function getPublicUrl($key = null) 342 | { 343 | $storageDisk = Storage::disk(config('filemanager.disk')); 344 | $url = $storageDisk->url('uploads/' . $this->uuid . '/' . $this->file_name); 345 | if (config('filemanager.files.' . $key)) { 346 | list($key, $resize, $size) = explode('.', $key); 347 | $extension = pathinfo($this->file_name, PATHINFO_EXTENSION); 348 | $name = str_replace('.' . $extension, '', $this->file_name); 349 | $url = $storageDisk->url('cache/' . $this->uuid . '/' . $name . '-' . $size . '.' . $extension); 350 | } 351 | 352 | return $url; 353 | } 354 | 355 | public function getIsPrivateAttribute() 356 | { 357 | return $this->is_private ? true : false; 358 | } 359 | 360 | public function getIsPublicAttribute() 361 | { 362 | return $this->is_private ? false : true; 363 | } 364 | 365 | public function getBasenameAttribute(): string 366 | { 367 | return $this->name . '.' . $this->extension; 368 | } 369 | 370 | public function __toString() 371 | { 372 | return "name: {$this->name}, size: {$this->size}, mime: {$this->mimeType}"; 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /src/Services/ImageService.php: -------------------------------------------------------------------------------- 1 | sizes = config('filemanager.images.sizes'); 19 | } 20 | 21 | protected function handleDelete(File $file) 22 | { 23 | if (is_null($this->getSizes())) { 24 | if ($sizes = $this->getConfig("sizes")) 25 | $this->setSizes($sizes); 26 | else 27 | $this->setSizes(["16", "24", "32", "64", "128"]); 28 | } 29 | 30 | if (is_null($this->getThumbSize())) { 31 | if (!$thumb = $this->getConfig("thumb")) 32 | $this->setThumbSize($thumb); 33 | else 34 | $this->setThumbSize("128"); 35 | } 36 | 37 | $sizes = $this->getSizes(); 38 | foreach ($sizes as $size) { 39 | $sizePath = $file->base_path . "{$size}/"; 40 | $sizePath = $sizePath . $file->file_name; 41 | if ($file->private) { 42 | $sizePath = storage_path($sizePath); 43 | } else { 44 | $sizePath = public_path($sizePath); 45 | } 46 | 47 | $this->disk->delete($sizePath); 48 | } 49 | 50 | $thumbSize = $file->base_path . "thumb/" . $file->file_name; 51 | $originalSize = $file->base_path . "original/" . $file->file_name; 52 | 53 | if ($file->private) { 54 | $thumbSize = storage_path($thumbSize); 55 | } else { 56 | $thumbSize = public_path($thumbSize); 57 | } 58 | 59 | if ($file->private) { 60 | $originalSize = storage_path($originalSize); 61 | } else { 62 | $originalSize = public_path($originalSize); 63 | } 64 | 65 | $this->disk->delete($thumbSize); 66 | $this->disk->delete($originalSize); 67 | 68 | return true; 69 | } 70 | 71 | /** 72 | * Crop the image (called via ajax). 73 | */ 74 | public function getCropimage($overWrite = true) 75 | { 76 | $image_name = request('img'); 77 | $image_path = $this->lfm->setName($image_name)->path('absolute'); 78 | $crop_path = $image_path; 79 | 80 | if (!$overWrite) { 81 | $fileParts = explode('.', $image_name); 82 | $fileParts[count($fileParts) - 2] = $fileParts[count($fileParts) - 2] . '_cropped_' . time(); 83 | $crop_path = $this->lfm->setName(implode('.', $fileParts))->path('absolute'); 84 | } 85 | 86 | event(new ImageIsCropping($image_path)); 87 | 88 | $crop_info = request()->only('dataWidth', 'dataHeight', 'dataX', 'dataY'); 89 | 90 | // crop image 91 | Image::make($image_path) 92 | ->crop(...array_values($crop_info)) 93 | ->save($crop_path); 94 | 95 | // make new thumbnail 96 | $this->lfm->makeThumbnail($image_name); 97 | 98 | event(new ImageWasCropped($image_path)); 99 | } 100 | 101 | public function getNewCropimage() 102 | { 103 | $this->getCropimage(false); 104 | } 105 | 106 | 107 | 108 | //public function resizeImagePost(Request $request) 109 | //{ 110 | // $this->validate($request, [ 111 | // 'title' => 'required', 112 | // 'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 113 | // ]); 114 | // 115 | // $image = $request->file('image'); 116 | // $input['imagename'] = time().'.'.$image->getClientOriginalExtension(); 117 | // 118 | // $destinationPath = public_path('/thumbnail'); 119 | // $img = Image::make($image->getRealPath()); 120 | // $img->resize(100, 100, function ($constraint) { 121 | // $constraint->aspectRatio(); 122 | // })->save($destinationPath.'/'.$input['imagename']); 123 | // 124 | // $destinationPath = public_path('/images'); 125 | // $image->move($destinationPath, $input['imagename']); 126 | // 127 | // $this->postImage->add($input); 128 | // 129 | // return back() 130 | // ->with('success','Image Upload successful') 131 | // ->with('imageName',$input['imagename']); 132 | //} 133 | 134 | 135 | public function makeImage(Image $image, array $sizeOption) 136 | { 137 | $sizeOption += [ 138 | 'width' => null, 139 | 'height' => null, 140 | 'fit' => null, 141 | ]; 142 | 143 | if (!$sizeOption['fit']) { 144 | return $image->resize($sizeOption['width'], $sizeOption['height'], function ($constraint) { 145 | $constraint->aspectRatio(); 146 | $constraint->upsize(); 147 | }); 148 | } 149 | 150 | if ($sizeOption['fit'] == 'crop') { 151 | $cropX = isset($sizeOption['x']) ? $sizeOption['x'] : null; 152 | $cropY = isset($sizeOption['y']) ? $sizeOption['y'] : null; 153 | 154 | $image->crop($sizeOption['width'], $sizeOption['height'], $cropX, $cropY, function ($constraint) { 155 | $constraint->upsize(); 156 | }); 157 | } elseif ($sizeOption['fit'] == 'max') { 158 | $image->resize($sizeOption['width'], $sizeOption['height'], function ($constraint) { 159 | $constraint->upsize(); 160 | }); 161 | } elseif ($sizeOption['fit'] == 'contain') { 162 | $image->resizeCanvas($sizeOption['width'], $sizeOption['height']); 163 | } elseif ($sizeOption['fit'] == 'stretch') { 164 | $image->resize($sizeOption['width'], $sizeOption['height'], function ($constraint) { 165 | $constraint->upsize(); 166 | }); 167 | } elseif ($sizeOption['fit'] == 'pad') { 168 | $width = $image->width(); 169 | $height = $image->height(); 170 | 171 | $color = isset($sizeOption['color']) ? $sizeOption['color'] : '#fff'; 172 | if ($width < $height) { 173 | $newHeight = $sizeOption['height']; 174 | $newWidth = ($width * 100) / $sizeOption['width']; 175 | 176 | $image->resize($newWidth, $newHeight, function ($constraint) { 177 | $constraint->aspectRatio(); 178 | }); 179 | $image->resizeCanvas($sizeOption['width'], $sizeOption['height'], 'center', false, $color); 180 | } elseif ($width > $height) { 181 | $newWidth = $sizeOption['width']; 182 | $newHeight = ($height * 100) / $sizeOption['height']; 183 | 184 | $image->resize($newWidth, $newHeight, function ($constraint) { 185 | $constraint->aspectRatio(); 186 | }); 187 | $image->resizeCanvas($sizeOption['width'], $sizeOption['height'], 'center', false, $color); 188 | } elseif ($width == $height) { 189 | $image->resize($sizeOption['width'], $sizeOption['height'], function ($constraint) { 190 | $constraint->aspectRatio(); 191 | }); 192 | $image->resizeCanvas($sizeOption['width'], $sizeOption['height'], 'center', false, $color); 193 | } 194 | } else { 195 | $image->fit($sizeOption['width'], $sizeOption['height'], function ($constraint) { 196 | $constraint->upsize(); 197 | }); 198 | } 199 | 200 | return $image; 201 | } 202 | 203 | public function resizeImage(Upload $upload, string $size) 204 | { 205 | $storage = \Storage::disk(config('upload.disk')); 206 | $config = config('upload.files.' . $upload->file_field . '.resize.' . $size); 207 | $path = $upload->uuid . '/'; 208 | $file = $storage->get('uploads/' . $path . $upload->file_name); 209 | 210 | $extension = pathinfo($upload->file_name, PATHINFO_EXTENSION); 211 | $name = str_replace('.' . $extension, '', $upload->file_name); 212 | 213 | $image = \Image::make($file); 214 | $image = $this->makeImage($image, $config); 215 | $image->encode($extension); 216 | $storage->put('cache/' . $path . $name . '-' . $size . '.' . $extension, $image->__toString()); 217 | } 218 | 219 | 220 | public function generateImages($fileField = null) 221 | { 222 | $uploads = Upload::query() 223 | ->where('has_reference', true) 224 | ->where('file_type', 'image') 225 | ->where(function ($query) use ($fileField) { 226 | if ($fileField) { 227 | $query->where('file_field', $fileField); 228 | } 229 | }) 230 | ->get(); 231 | 232 | foreach ($uploads as $upload) { 233 | foreach (config('upload.files.' . $upload->file_field . '.resize') as $size => $options) { 234 | $this->resizeImage($upload, $size); 235 | } 236 | } 237 | } 238 | 239 | public function optimizeUploadedImages($fileField = null) 240 | { 241 | $storage = \Storage::disk(config('upload.disk')); 242 | 243 | $uploads = Upload::query() 244 | ->where('file_type', 'image') 245 | ->where(function ($query) use ($fileField) { 246 | if ($fileField) { 247 | $query->where('file_field', $fileField); 248 | } 249 | }) 250 | ->get(); 251 | 252 | foreach ($uploads as $upload) { 253 | foreach ($storage->allFiles('uploads/' . $upload->uuid) as $file) { 254 | ImageOptimizer::optimize($storage->path($file)); 255 | } 256 | 257 | foreach ($storage->allFiles('cache/' . $upload->uuid) as $file) { 258 | ImageOptimizer::optimize($storage->path($file)); 259 | } 260 | } 261 | } 262 | 263 | public function optimizeImages($path) 264 | { 265 | $images = ['jpg', 'jpeg', 'png', 'gif', 'svg']; 266 | $path = base_path($path); 267 | 268 | if (!is_dir($path)) { 269 | throw new \Exception('Directory Not found.'); 270 | } 271 | 272 | $files = \File::allfiles($path); 273 | 274 | foreach ($files as $file) { 275 | //optimize images only 276 | if (!in_array(strtolower($file->getExtension()), $images)) { 277 | continue; 278 | } 279 | 280 | ImageOptimizer::optimize($file->getRealPath()); 281 | } 282 | } 283 | 284 | 285 | /** 286 | * resize image and return specific array of images 287 | * 288 | * @param $filePath 289 | * @param $uploadPath 290 | * @param $fileName 291 | * @return mixed 292 | */ 293 | protected function resize($filePath, $uploadPath, $fileName) 294 | { 295 | if (is_null($this->getSizes())) { 296 | if ($sizes = $this->getConfig("sizes")) 297 | $this->setSizes($sizes); 298 | else 299 | $this->setSizes(["16", "24", "32", "64", "128"]); 300 | } 301 | 302 | if (is_null($this->getThumbSize())) { 303 | if (!$thumb = $this->getConfig("thumb")) 304 | $this->setThumbSize($thumb); 305 | else 306 | $this->setThumbSize("128"); 307 | } 308 | 309 | $sizes = $this->getSizes(); 310 | foreach ($sizes as $size) { 311 | $sizeUploadPath = $uploadPath . "{$size}/"; 312 | if (!is_dir($sizeUploadPath)) mkdir($sizeUploadPath); 313 | $sizeName = $sizeUploadPath . $fileName; 314 | \Intervention\Image\Facades\Image::make($filePath)->fit($size, $size, function ($constraint) { 315 | $constraint->aspectRatio(); 316 | // $constraint->upsize(); 317 | })->save($sizeName); 318 | } 319 | 320 | $thumbUploadPath = $uploadPath . "thumb/"; 321 | if (!is_dir($thumbUploadPath)) mkdir($thumbUploadPath); 322 | $thumbPath = $thumbUploadPath . $fileName; 323 | copy($uploadPath . "{$this->getThumbSize()}/" . $fileName, $thumbPath); 324 | 325 | return $this; 326 | } 327 | 328 | public function preview($disk, $path) 329 | { 330 | // get image 331 | $preview = Image::make($this->disk($disk)->get($path)); 332 | 333 | return $preview->response(); 334 | } 335 | 336 | 337 | public function url($disk, $path) 338 | { 339 | return [ 340 | 'result' => [ 341 | 'status' => 'success', 342 | 'message' => null, 343 | ], 344 | 'url' => $this->disk->disk($disk)->url($path), 345 | ]; 346 | } 347 | 348 | // use Spatie\Image\Image; 349 | 350 | // class ImageFactory 351 | // { 352 | // public static function load(string $path): Image 353 | // { 354 | // return Image::load($path)->useImageDriver(config('media-library.image_driver')); 355 | // } 356 | // } 357 | 358 | // 359 | //class ImageRequest extends FormRequest 360 | //{ 361 | // use ConvertsBase64ToFiles; 362 | // 363 | // protected function base64FileKeys(): array 364 | // { 365 | // return [ 366 | // 'jpg_image' => 'Logo.jpg', 367 | // ]; 368 | // } 369 | // 370 | // public function rules() 371 | // { 372 | // return [ 373 | // 'jpg_image' => ['required', 'file', 'image'], 374 | // ]; 375 | // } 376 | //} 377 | // 378 | // 379 | //trait ConvertsBase64ToFiles 380 | //{ 381 | // protected function base64FileKeys(): array 382 | // { 383 | // return []; 384 | // } 385 | // 386 | // /** 387 | // * Pulls the Base64 contents for each image key and creates 388 | // * an UploadedFile instance from it and sets it on the 389 | // * request. 390 | // * 391 | // * @return void 392 | // */ 393 | // function prepareForValidation() 394 | // { 395 | // Collection::make($this->base64FileKeys())->each(function ($filename, $key) { 396 | // rescue(function () use ($key, $filename) { 397 | // $base64Contents = $this->input($key); 398 | // 399 | // if (!$base64Contents) { 400 | // return; 401 | // } 402 | // 403 | // // Generate a temporary path to store the Base64 contents 404 | // $tempFilePath = tempnam(sys_get_temp_dir(), $filename); 405 | // 406 | // // Store the contents using a stream, or by decoding manually 407 | // if (Str::startsWith($base64Contents, 'data:') && count(explode(',', $base64Contents)) > 1) { 408 | // $source = fopen($base64Contents, 'r'); 409 | // $destination = fopen($tempFilePath, 'w'); 410 | // 411 | // stream_copy_to_stream($source, $destination); 412 | // 413 | // fclose($source); 414 | // fclose($destination); 415 | // } else { 416 | // file_put_contents($tempFilePath, base64_decode($base64Contents, true)); 417 | // } 418 | // 419 | // $uploadedFile = new UploadedFile($tempFilePath, $filename, null, null, true); 420 | // 421 | // $this->request->remove($key); 422 | // $this->files->set($key, $uploadedFile); 423 | // }, null, false); 424 | // }); 425 | // } 426 | } 427 | --------------------------------------------------------------------------------