// +---------------------------------------------------------------------- namespace ywxapp\service; use think\Exception; use think\facade\Config; use think\file\UploadedFile; use ywxapp\utils\storage\StorageInterface; /** * FileStorageService 类 * * @author ywxapp */ class FileStorageService { // 驱动常量 const DRIVER_LOCAL = 'local'; const DRIVER_ALIOSS = 'alioss'; const DRIVER_QCOS = 'qcos'; protected $config; protected $storage; /** * 构造函数,读取配置并初始化驱动 */ public function __construct() { // 读取配置文件 $this->config = Config::get('filesystem', []); // 获取当前配置的驱动名称 $driverName = $this->config['default'] ?? self::DRIVER_LOCAL; // 实例化对应的驱动 $this->initDriver($driverName); } /** * 初始化存储驱动 */ protected function initDriver(string $driverName): void { $drivers = $this->config['disks'] ?? []; if (! isset($drivers[$driverName])) { throw new Exception("存储驱动配置不存在: {$driverName}"); } $config = $drivers[$driverName]; $class = $config['class']; if (! class_exists($class)) { throw new Exception("存储驱动类不存在: {$class}"); } $this->storage = new $class($config); if (! $this->storage instanceof StorageInterface) { throw new Exception("存储驱动必须实现 StorageInterface 接口"); } } /** * 上传文件(对外调用的方法) */ public function upload(UploadedFile $file, string $path = '', array $options = []): array { // 1. 基础验证(例如文件大小、类型) $this->validateFile($file, $options); // 2. 如果未指定路径,生成默认路径 if (empty($path)) { $path = $this->generatePath($file); } // throw new \think\Exception(json_encode( [ // $file->getoriginalName(), //获取文件原始名称带后缀 // $file->getSize(), //获取文件大小 // $file->extension(), //获取文件后缀 // ]) // , 403); // 3. 调用具体驱动的上传方法 $data = [ 'module' => $options['module'] ?? 'index', // 模块标识 'original' => $file->getoriginalName(), 'size' => $file->getSize(), 'ext' => $file->extension(), 'mime' => $file->getOriginalMime(), 'mime' => $file->getMime(), 'storage' => $this->config['default'] ?? self::DRIVER_LOCAL, 'is_image' => (int) str_starts_with($file->getMime(), 'image/'), 'upload_ip' => request()->ip(), ]; // 如果是图片且能获取到尺寸 if ($data['is_image'] && function_exists('getimagesize')) { // 注意:如果是云存储,这里可能需要从临时文件获取尺寸 $tempPath = $file->getRealPath(); if (file_exists($tempPath)) { $imgInfo = getimagesize($tempPath); $data['width'] = $imgInfo[0] ?? 0; $data['height'] = $imgInfo[1] ?? 0; } } list($name, $path, $url) = $this->storage->upload($file, $path, $options); $data['name'] = $name; $data['path'] = $path; $data['url'] = $url; return $data; } /** * 删除文件 */ public function delete(string $path): bool { return $this->storage->delete($path); } /** * 获取URL */ public function getUrl(string $path): string { return $this->storage->getUrl($path); } /** * 基础文件验证 */ protected function validateFile(UploadedFile $file, array $options): void { // 使用验证器验证上传的文件 validate( [ 'file' => [ // 限制文件大小(单位b),这里限制为4M 'fileSize' => 4 * 1024 * 1024, // 限制文件后缀,多个后缀以英文逗号分割 'fileExt' => 'gif,jpg,png', // fileSize 上传文件的最大字节; // fileExt 文件后缀,多个用逗号分割或者数组; // fileMime 文件MIME类型,多个用逗号分割或者数组; // image 验证图像文件的尺寸和类型, ], ], [ 'file.fileSize' => '文件太大', 'file.fileExt' => '不支持的文件后缀', ] )->check(['file' => $file]); } /** * 生成唯一文件路径 */ protected function generatePath(UploadedFile $file): string { $ext = $file->getClientOriginalExtension(); $date = date('Y/m/d'); $filename = date('His') . '_' . uniqid() . ".{$ext}"; return "{$date}/{$filename}"; } /** * 魔术方法,允许直接调用驱动的其他特有方法 */ public function __call($method, $parameters) { if (method_exists($this->storage, $method)) { return call_user_func_array([$this->storage, $method], $parameters); } throw new \BadMethodCallException("Method {$method} does not exist."); } }