['image/jpeg', 'image/gif', 'image/bmp', 'image/png'], 'audio' => ['audio/amr', 'audio/aac', 'audio/ogg', 'audio/mpeg'], 'video' => ['video/mp4', 'video/mpeg', 'video/x-flv', 'video/3gpp', 'video/quicktime', 'video/mov','video/x-m4v'] ]; public static $extensions = [ 'image' => ['png', 'jpg', 'jpeg', 'gif', 'bmp'], 'audio' => ['amr', 'ogg', 'mpeg'], 'video' => ['mp4', 'mpeg', 'flv', '3gp', 'mov'] ]; /** * 构造函数 * * @return void */ public function __construct(Request $request, FileRepository $fileRepository, FileWithRepository $fileWithRepository) { $this->user = $request->user(); $this->fileRepository = $fileRepository; $this->fileWithRepository = $fileWithRepository; } /** * 文件列表 * * @param array $conditions * @return mixed */ public function index(array $conditions = []) { $limit = $conditions['limit'] ?? 20; return $this->fileRepository->with('file_withs')->applyConditions()->paginate($limit); } /** * 删除 * * @return bool */ public function destroy($ids) { $ids = is_array($ids) ? $ids : [$ids]; if ($this->fileWithRepository->whereIn('file_id', $ids)->count()) { throw new BeUsedException('文件已被使用, 不能删除'); } $this->fileRepository->destroy($ids); return true; } /** * 使用type, type_id查找文件 * * @param string $type * @param string|int $typeIds * @return void */ public function fetchByType($type, $typeIds, $force = false) { if ($force) { $this->fileWithRepository->forgetCached(); } $files = $this->fileWithRepository->with('file')->withType($type, $typeIds)->get(); $files->map(function ($item) { if ($file = $item->file) { $file->url = cdn($item->disk, $item->filename, $item->created_at); $file->cover = $item->thumb ? cdn($item->disk, $this->generateFilePath($item->filename), $item->created_at) : ''; $item->file = $file; } }); return $files; } /** * 储存上传文件 * * @param UploadedFile $file * * @throws UploadFailedException * @return mixed */ public function upload(UploadedFile $file, $input = [], $disk = null) { $disk = $disk ?: config('filesystems.default'); if ($input['filename']) { $fileModel = $this->store($file, $input, $disk); } else { $fileModel = $this->validateFileInDatabase($file, function (UploadedFile $file, string $md5) use ($input, $disk) { $input['filename'] = $this->generateFilePath($md5); return $this->store($file, $input, $disk); }); } if ($input['type'] && $input['typeid'] && in_array($input['type'], self::$types)) { $this->apply($fileModel->id, $input['type'], $input['typeid'], $input['cover'] ?? false); } return $fileModel; } /** * 添加文件关联 * * @param array|int $ids * @param string $type * @param int|string $typeid * @return void */ public function apply($ids, $type, $typeid, $cover = false) { if (!in_array($type, self::$types)) { throw new NotAllowedException('不允许的上传类型'); } $ids = is_array($ids) ? $ids : [$ids]; if (empty($ids)) { return false; } if ($this->user instanceof User) { $column = 'uid'; } if ($this->user instanceof Account) { $column = 'account_id'; } $news = []; $existed_ids = $this->fileRepository->select('id')->whereIn('id', $ids)->get()->pluck('id')->toArray(); foreach ($ids as $id) { if (!in_array($id, $existed_ids)) { continue; } $new = [ 'file_id' => $id, 'account_id' => 0, 'uid' => 0, 'type' => $type, 'type_id' => $typeid, 'created_at' => time(), 'updated_at' => time(), ]; if ($column) { $new[$column] = $this->user->id; } $news[] = $new; } if (empty($news)) { return false; } if ($cover) { $this->detach($type, $typeid); } return $this->fileWithRepository->insert($news); } /** * 取消文件关联 * * @param string $type * @param int|string $typeid * @return boolean */ public function detach($type, $typeid) { $this->fileWithRepository->withType($type, $typeid)->delete(); return true; } /** * 缩略图文件名 * * @param string $filename * @return string */ public static function getThumb($filename) { return $filename.'.thumb.jpg'; } /** * 验证文件是否已上传过,返回FileModel实例 * * @param UploadedFile $file * @param callable $call * @return FileModel */ protected function validateFileInDatabase(UploadedFile $file, callable $call): ?FileModel { $hash = md5_file($file->getRealPath()); return $this->fileRepository->where('hash', $hash)->firstOr(function () use ($file, $call, $hash) { return call_user_func_array($call, [$file, $hash]); }); } /** * 存储文件 * * @param UploadedFile $file * @param array $input * @param string $disk * @return mixed */ protected function store(UploadedFile $file, $input, $disk) { $disk = $disk ?: config('filesystems.default'); $clientHeight = $input['height'] ?? 0; $clientWidth = $input['width'] ?? 0; $clientDuration = $input['duration'] ?? 0; $hash = md5_file($file->getRealPath()); $path = $input['filename'] ?? $this->generateFilePath($hash); // 图片做旋转处理 if (in_array($file->getClientMimeType(), ['image/jpeg', 'image/bmp', 'image/png'])) { ini_set('memory_limit', '-1'); try { Image::make($file->getRealPath())->orientate()->save($file->getRealPath(), 100); } catch (\Exception $e) { } } list($width, $height) = ($imageInfo = @getimagesize($file->getRealPath())) === false ? [null, null] : $imageInfo; if (($filename = $file->storeAs($path, '', $disk)) === false) { throw new UploadFailedException('上传失败'); } $new = [ 'hash' => $hash, 'disk' => $disk, 'filename' => $filename, 'origin_filename' => $file->getClientOriginalName(), 'mimetype' => $file->getClientMimeType(), 'filesize' => $file->getClientSize(), 'width' => $width ?? $clientWidth, 'height' => $height ?? $clientHeight, 'duration' => $clientDuration, ]; $fileModel = $this->fileRepository->create($new); return $fileModel; } /** * 根据文件md5生成文件路径 * * @param string $hash * @return void */ protected function generateFilePath($hash) { return Carbon::now()->format('Y/m/d') . $hash; } }