From ca6abacb5decaea59e3699bba3fd99e81047bb22 Mon Sep 17 00:00:00 2001 From: u2nyakim Date: Tue, 26 Aug 2025 17:56:48 +0800 Subject: [PATCH] up. --- .../admin/system/FileController.php | 6 + app/entity/SysFileRule.php | 14 ++ app/entity/SysUserFile.php | 14 ++ app/entity/UploadFile.php | 18 ++ app/model/SysFileRule.php | 1 - app/model/SysUserFile.php | 1 - app/service/FileService.php | 49 ++++ app/service/file/FilesystemUploadService.php | 223 ++++++++++++++++++ route/api.php | 5 +- 9 files changed, 327 insertions(+), 4 deletions(-) create mode 100644 app/entity/SysFileRule.php create mode 100644 app/entity/SysUserFile.php create mode 100644 app/entity/UploadFile.php create mode 100644 app/service/FileService.php create mode 100644 app/service/file/FilesystemUploadService.php diff --git a/app/controller/admin/system/FileController.php b/app/controller/admin/system/FileController.php index 40a3861..296777f 100644 --- a/app/controller/admin/system/FileController.php +++ b/app/controller/admin/system/FileController.php @@ -38,4 +38,10 @@ class FileController extends BaseController SysFileRecord::destroy($data); return $this->writeSuccess('删除成功'); } + + public function upload() + { + $file = $this->request->file('file'); + + } } \ No newline at end of file diff --git a/app/entity/SysFileRule.php b/app/entity/SysFileRule.php new file mode 100644 index 0000000..121f528 --- /dev/null +++ b/app/entity/SysFileRule.php @@ -0,0 +1,14 @@ +file = $file; + $this->record = $record; + } +} \ No newline at end of file diff --git a/app/model/SysFileRule.php b/app/model/SysFileRule.php index e0f83cf..c7e80bf 100644 --- a/app/model/SysFileRule.php +++ b/app/model/SysFileRule.php @@ -2,7 +2,6 @@ namespace app\model; -use app\adminapi\model\TenantModel; use app\BaseModel; /** diff --git a/app/model/SysUserFile.php b/app/model/SysUserFile.php index ebcbf47..04c80cd 100644 --- a/app/model/SysUserFile.php +++ b/app/model/SysUserFile.php @@ -2,7 +2,6 @@ namespace app\model; -use app\adminapi\model\TenantModel; use app\BaseModel; use think\model\concern\SoftDelete; use think\model\relation\HasOne; diff --git a/app/service/FileService.php b/app/service/FileService.php new file mode 100644 index 0000000..aa2b448 --- /dev/null +++ b/app/service/FileService.php @@ -0,0 +1,49 @@ +app->bind('filesystem', $this); + } + + public function upload(string $contextId, File $file, SysFileRule $rule): UploadFile + { + $sysFileRecord = new SysFileRecord; + // 存储规则相关 + $sysFileRecord->set('rid', $rule->id); // ruleId + $sysFileRecord->set('disk', $rule->disk); // 存储disk + // 文件信息相关 + $sysFileRecord->set('path',''); // 文件存储路径 + $sysFileRecord->set('name', ''); // 文件名称 + $sysFileRecord->set('length', (int)$file->getSize()); // 文件大小 + $sysFileRecord->set('content_type', $file->getMime()); // 文件类型(mime) + $sysFileRecord->set('md5', $file->md5());// 文件md5 + $sysFileRecord->set('sha1', $file->sha1()); // 文件sha1 + $sysFileRecord->set('extension', $file->extension()); // 文件扩展名(extension) + $sysFileRecord->set('origin_name', $file->getFilename()); // 文件原始名称 + $sysFileRecord->set('create_time', date('Y-m-d H:i:s')); // 文件记录创建时间 + $sysFileRecord->set('create_date', date('Y-m-d')); // 文件记录创建日期 + // 关联数据相关 + $sysFileRecord->set('context_id', $contextId); // 来源上下文 + + // 返回UploadFile实体类 + $uploadFile = new UploadFile(); + $uploadFile->done($file, $sysFileRecord); + return $uploadFile; + } +} \ No newline at end of file diff --git a/app/service/file/FilesystemUploadService.php b/app/service/file/FilesystemUploadService.php new file mode 100644 index 0000000..647520e --- /dev/null +++ b/app/service/file/FilesystemUploadService.php @@ -0,0 +1,223 @@ + 0) { + $directory = SysUserFile::where('is_directory', 1)->find($directory_id); + if (empty($directory)) { + throw new ValidateException('Dir: 目录不存在'); + } + } + /** + * @var SysFileRule $fileRule + */ + $fileRule = SysFileRule::find($rule_id); + if (empty($fileRule) || $fileRule['is_close']) { + throw new ValidateException('Rule: 已关闭上传权限'); + } + + /* + * 验证文件信息 + */ + if ($fileRule->rule_file_size_limit_min > 0) { + if ($file->getSize() < $fileRule->rule_file_size_limit_min * 1024) { + throw new ValidateException('文件限制:文件太小了'); + } + } + $validateRule = []; + if ($fileRule->rule_file_size_limit_max > 0) { + $validateRule[] = 'fileSize:' . ($fileRule->rule_file_size_limit_max * 1024); + } + if ($fileRule->rule_file_ext !== '') { + $validateRule[] = "fileExt:$fileRule->rule_file_ext"; + } + if ($fileRule->rule_file_mime !== '') { + $validateRule[] = "fileMime:$fileRule->rule_file_mime"; + } + if ($fileRule->rule_image !== '') { + $validateRule[] = "image:$fileRule->rule_image"; + } + if ($validateRule) { + validate(['file' => implode('|', $validateRule)]) + ->check(['file' => $file]); + } + $putDisk = $fileRule->disk; + $putUrl = $fileRule->url; + $putPath = $fileRule->path_rule; + $nameRule = $fileRule->name_rule; + + + $nameRule = $this->replaceName($nameRule, $file, $vars + ['uid' => $user_id]); + $putPath = $this->replaceName($putPath, $file, $vars + ['uid' => $user_id]); + + $savePath = ($putPath ? trim($putPath, '/') : '') . '/' . $nameRule; + + $fileName = pathinfo($savePath, PATHINFO_FILENAME); + $filePath = pathinfo($savePath, PATHINFO_DIRNAME); + +// dump(['#1', $putPath, $nameRule]); +// dump(['#2', $filePath, $fileName]); + + $filesystemSrv = Filesystem::disk($putDisk); + $uploadPath = $filesystemSrv->putFile($filePath, $file, function () use ($fileName) { + return $fileName; + }); + if ($uploadPath === false) { + throw new FileException('上传失败'); + } + $diskConfig = Filesystem::getDiskConfig($putDisk); + $url = $diskConfig['url'] ?? ''; + if ($url && Validate::url($url) === false) { + $url = trim($url, '/'); + $uploadPath = "$url/$uploadPath"; + $url = ""; + } + $filesystem_apiUrl = $this->app->config->get('filesystem.api_url'); + /* + * URL取值优先级为 + * #1 取fileRule中的设置 -> #2 取config中的设置 -> #3 取api_url的配置 #4 取当前域名的配置 + * 注意, fileRule中的permissions配置会把#3,#4做为优先项 + */ + $diskUrl = $putUrl ?: ($url ?: $filesystem_apiUrl ?: Request::domain()); + $uploadSavePath = pathinfo($uploadPath, PATHINFO_DIRNAME); + $fileSaveName = pathinfo($uploadPath, PATHINFO_BASENAME); +// dump([$diskUrl, $uploadPath]); +// dump(['#4', $uploadSavePath, $fileSaveName]); + $filesystem_model = $this->app->config->get('filesystem.model'); + if (empty($filesystem_model)) $filesystem_model = SysFileRecord::class; + + $sysFileRecord = $this->app->invokeClass($filesystem_model); + $sysFileRecord->save([ + 'name' => $fileSaveName, + 'origin_name' => $file->getOriginalName(), + 'alias_name' => '', + 'path' => $uploadSavePath, + 'length' => $file->getSize(), + 'extension' => $file->getOriginalExtension(), + 'content_type' => mime_content_type($file->getRealPath()) ?: '', + 'md5' => $file->md5(), + 'sha1' => $file->sha1(), + 'disk' => $putDisk, + 'uid' => $user_id, + 'create_user_id' => $user_id, + 'context_id' => $this->app->request->contextId, + 'gid' => 0, + 'create_date' => date('Y-m-d'), + 'create_time' => date('Y-m-d H:i:s'), + 'update_time' => date('Y-m-d H:i:s'), + 'delete_time' => null, + 'uuid' => guid() + ]); + $sysFileRecordData = $sysFileRecord->toArray(); + $fileRuleData = $fileRule->toArray(); + + if ($user_id > 0) { + (new SysUserFile)->save([ + 'user_id' => $user_id, + 'name' => $file->getOriginalName(), + 'is_directory' => 0, + 'parent_id' => $directory_id, + 'path' => $uploadPath, + 'length' => $file->getSize(), + 'content_type' => $file->getOriginalMime(), + 'deleted' => 0, + // 租户 + 'tenant_id' => $this->app->request->tenantId, + // 磁盘 + 'disk' => $putDisk, + // 存储规则 + 'rule_id' => $rule_id, + 'uuid' => $sysFileRecord->uuid, + 'url' => "$diskUrl/{$uploadPath}", + 'is_anonymous' => 0, + ]); + } + // 是否需要安全访问 + if ($fileRule->permissions) { + $uploadPath = "i/" . $sysFileRecord->uuid; + $diskUrl = $filesystem_apiUrl ?: Request::domain(); + } + $results = [ + // 文件对外显示URL + 'url' => "$diskUrl/{$uploadPath}", + // 文件对外显示存储路径 + 'path' => $uploadPath, + // 文件唯一标识符 + 'uuid' => "{$rule_id}@" . $sysFileRecord->uuid, + // 文件上传时间戳 + 'time' => time(), + ]; + // 触发文件上传事件 + Event::trigger('upload', ['results' => $results, 'file' => $file, 'rule' => $fileRuleData, 'model' => $sysFileRecordData, 'directory_id' => $directory_id]); + // 触发磁盘上传事件 + Event::trigger("upload.$putDisk", ['results' => $results, 'file' => $file, 'rule' => $fileRuleData, 'model' => $sysFileRecordData, 'directory_id' => $directory_id]); + // 触发磁盘某个行为上传事件 + Event::trigger("upload.rule_$rule_id", ['results' => $results, 'file' => $file, 'rule' => $fileRuleData, 'model' => $sysFileRecordData, 'directory_id' => $directory_id]); + return $results; + } + + + private function replaceName(string $pathname, UploadedFile $file, $vars = []): string + { + $array = [ + '{Y}' => date('Y'), + '{y}' => date('y'), + '{m}' => date('m'), + '{d}' => date('d'), + '{H}' => date('H'), + '{i}' => date('i'), + '{s}' => date('s'), + '{G}' => date('G'), + '{Ym}' => date('Ym'), + '{Ymd}' => date('Ymd'), + '{YmdH}' => date('YmdH'), + '{timestamp}' => time(), + '{uniqid}' => uniqid(), + '{md5}' => md5(microtime() . Str::random()), + '{md5-16}' => substr(md5(microtime() . Str::random()), 0, 16), + '{str-random-16}' => Str::random(16), + '{str-random-10}' => Str::random(10), + '{str-random-6}' => Str::random(6), + '{fmd5}' => $file->md5(), + '{fsha1}' => $file->sha1(), + '{fname}' => $file->getOriginalName(), + '{uid}' => $vars['uid'] ?? 'UID', + ]; + // 系统内部的key不允许被覆盖 + $useKeys = array_keys($array); + foreach ($vars as $key => $value) { + if (in_array($key, $useKeys)) { + continue; + } + $array["{{$key}}"] = $value; + } + return str_replace(array_keys($array), array_values($array), $pathname); + } +} \ No newline at end of file diff --git a/route/api.php b/route/api.php index d1fdc6a..3a699c7 100644 --- a/route/api.php +++ b/route/api.php @@ -45,8 +45,9 @@ Route::group("adminapi", function () { /* * 文件管理 */ - Route::get('page', [FileController::class, "page"])->name("system.pageFiles"); - Route::delete('remove/batch', [FileController::class, "batchRemove"])->name("system.batchRemoveFile"); + Route::get('page', [FileController::class, "page"])->name("filesystem.pageFiles"); + Route::delete('remove/batch', [FileController::class, "batchRemove"])->name("filesystem.batchRemoveFile"); + Route::post('upload', [FileController::class, "upload"])->name("filesystem.uploadFile"); })->name('文件接口');