app = $app; } public function getDownloadUrl($fileId) { return $this->getFilesystemApiUrl()."/i/down/".hashids(4)->encode($fileId)."?token=".md5(uniqid()); } public function getFilesystemApiUrl() { $filesystem_apiUrl = $this->app->config->get('filesystem.api_url'); if ($filesystem_apiUrl == "#") { $filesystem_apiUrl = Request::domain(); } else if (!Validate::url($filesystem_apiUrl)) { throw new FileException('请正确配置filesystem.api_url'); } return $filesystem_apiUrl; } public function getFileUrl($rule_id, $file_path) { $rule = SysFileRule::find($rule_id); if ($rule) { $putUrl = $rule->url ? $rule->url : $this->getFilesystemApiUrl(); return $putUrl.$file_path; } return ""; } /** * @param UploadedFile $file 上传的文件 * @param int $directory_id 存储的目录位置 * @param int $user_id 用户ID * @param array $vars * @return UploadResult * @throws DataNotFoundException * @throws DbException * @throws ModelNotFoundException */ public function upload(UploadedFile $file, int $directory_id, int $user_id, array $vars = []): UploadResult { $rule_id = 1; if ($directory_id > 0) { // 上传到某个目录下 $directory = SysUserFile::where('is_directory', 1)->find($directory_id); if (empty($directory)) { throw new ValidateException('Dir: 目录不存在'); } } /* * 文件记录实体 * filesystem.model */ $filesystem_model = $this->app->config->get('filesystem.model'); if (empty($filesystem_model)) { throw new FileException('File记录模型未定义'); } /* * 文件预览接口(用于安全访问模式) * filesystem.api_url */ $filesystem_apiUrl = $this->app->config->get('filesystem.api_url'); if (empty($filesystem_apiUrl)) { throw new FileException('请正确配置filesystem.api_url'); } if ($filesystem_apiUrl == "#") { $filesystem_apiUrl = Request::domain(); } else if (!Validate::url($filesystem_apiUrl)) { throw new FileException('请正确配置filesystem.api_url'); } /** * @var SysFileRule|\app\model\SysFileRule $rule */ $rule = SysFileRule::find($rule_id); if (empty($rule)) { throw new ValidateException('Rule: 上传规则不存在'); } if ($rule->is_close) { throw new ValidateException('Rule: 已关闭上传权限'); } /* * 根据规则校验上传的文件是否符合要求 */ $this->checkFileRule($rule, $file); $putDisk = $rule->disk; $putUrl = $rule->url ? $rule->url : $filesystem_apiUrl; $putPath = $rule->path_rule; $nameRule = $rule->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); $disk = Filesystem::disk('public'); $uploadPath = $this->uploadFile($disk, $filePath, $file, $fileName); if ($uploadPath === false) { throw new FileException('上传失败'); } $uploadPath = $disk->url($uploadPath); $fileUrl = $putUrl . '/' . ltrim($uploadPath, '/'); $uploadSavePath = pathinfo($uploadPath, PATHINFO_DIRNAME); $fileSaveName = pathinfo($uploadPath, PATHINFO_BASENAME); // 系统文件上传日志记录 $sysFileRecord = $this->app->invokeClass($filesystem_model); $sysFileRecord->save([ 'rule_id' => $rule->id, 'name' => $fileSaveName, 'path' => $uploadSavePath, 'length' => $file->getSize(), 'extension' => $file->getOriginalExtension(), 'content_type' => mime_content_type($file->getRealPath()) ?: '', 'md5' => $file->md5(), 'disk' => $putDisk, 'create_user_id' => $user_id, 'context_id' => $this->app->request->contextId, '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, ]); // 是否需要安全访问 if ($rule->permissions) { $fileId = hashids(12)->encode($sysFileRecord->id); $uploadPath = "i/" . $fileId; $fileUrl = $putUrl . '/' . ltrim($uploadPath, '/'); } (new SysUserFile)->save([ 'rule_id' => $rule_id, 'disk' => $putDisk, '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, 'url' => $fileUrl, 'is_anonymous' => 0, ]); /* * 上传结果封装 */ $results = new UploadResult([ // 文件对外显示URL 'url' => $fileUrl, // 文件对外显示存储路径 'path' => $uploadPath, // 文件上传时间戳 'time' => time(), ]); /* * 触发上传完成事件 */ $this->triggerUploaded($results, $putDisk, $file, $rule, $sysFileRecord); 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); } private function checkFileRule(SysFileRule|\app\model\SysFileRule $fileRule, UploadedFile $file): void { /* * 验证文件信息 */ 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]); } } /** * 触发上传完成事件 * @param UploadResult $results 上传结果 * @param string $putDisk 存储磁盘 * @param UploadedFile $file 上传的文件 * @param SysFileRule $rule 上传规则 * @param SysFileRecord $sysFileRecord 文件上传记录 * @return void */ private function triggerUploaded(UploadResult $results, string $putDisk, UploadedFile $file, SysFileRule $rule, SysFileRecord $sysFileRecord): void { $eventData = ['results' => $results, 'file' => $file, 'rule' => $rule, 'model' => $sysFileRecord]; // 触发文件上传事件 Event::trigger('uploaded', $eventData); // 触发磁盘上传事件 Event::trigger("uploaded.$putDisk", $eventData); // 触发磁盘某个规则的上传事件 if (!empty($rule->id)) { Event::trigger("uploaded.rule_{$rule->id}", $eventData); } } private function uploadFile(Driver $driver, string $filePath, UploadedFile $file, string $fileName): bool|string { return $driver->putFile($filePath, $file, function () use ($fileName) { return $fileName; }); } }