authUser(); $file = $this->request->file('file'); if (!$file) { return $this->error('请选择文件'); } $mime = $file->getMime() ?: mime_content_type($file->getPathname()); $fileType = $this->detectFileType($mime); if (!PermissionService::canUpload($user, $fileType)) { return $this->error('您没有权限上传此类型文件或功能未开启', 403); } $maxSize = PermissionService::getMaxUploadSizeMb($user) * 1024 * 1024; if ($file->getSize() > $maxSize) { return $this->error('文件大小超出限制'); } if (!$this->isAllowedMime($mime, $fileType)) { return $this->error('不支持的文件类型'); } $originalName = $file->getOriginalName(); $fileSize = $file->getSize(); $uploadPath = config('upload.path'); if (!is_dir($uploadPath)) { mkdir($uploadPath, 0755, true); } $ext = $file->extension() ?: 'bin'; $subdir = date('Y/m/d'); $storedBase = uniqid() . '.' . $ext; $storedName = $subdir . '/' . $storedBase; $fullDir = $uploadPath . '/' . $subdir; if (!is_dir($fullDir)) { mkdir($fullDir, 0755, true); } $file->move($fullDir, $storedBase); $record = UploadFile::create([ 'user_id' => $user['id'], 'original_name' => $originalName, 'stored_name' => $storedBase, 'file_path' => $storedName, 'mime_type' => $mime, 'file_size' => $fileSize, 'file_type' => $fileType, ]); return $this->success([ 'id' => $record->id, 'url' => '/api/uploads/' . urlencode($storedBase), 'name' => $originalName, 'type' => $fileType, 'mime' => $mime, 'size' => $fileSize, ]); } public function serve($filename) { $filename = basename($filename); $upload = UploadFile::where('stored_name', $filename) ->whereOr('file_path', 'like', '%/' . $filename) ->find(); if (!$upload) { throw new HttpResponseException(json([ 'code' => 1, 'message' => '文件不存在', 'data' => null, ], 404)); } $path = config('upload.path') . '/' . $upload->file_path; if (!is_file($path)) { throw new HttpResponseException(json([ 'code' => 1, 'message' => '文件不存在', 'data' => null, ], 404)); } return download($path, $upload->original_name, true) ->mimeType($upload->mime_type); } private function detectFileType(string $mime): string { if (str_starts_with($mime, 'image/')) { return 'image'; } if (str_starts_with($mime, 'video/')) { return 'video'; } if (str_starts_with($mime, 'audio/')) { return 'audio'; } if (in_array($mime, config('upload.allowed_documents'), true)) { return 'document'; } return 'other'; } private function isAllowedMime(string $mime, string $fileType): bool { $config = config('upload'); return match ($fileType) { 'image' => in_array($mime, $config['allowed_images'], true), 'video' => in_array($mime, $config['allowed_videos'], true), 'audio' => in_array($mime, $config['allowed_audios'], true), 'document' => in_array($mime, $config['allowed_documents'], true), default => false, }; } }