config = $config; return $this; } public function uploadImage($filePath, string $key): bool|array { return $this->uploadFile($filePath, $key); } public function uploadVideo($filePath, string $key): bool|array { return $this->uploadFile($filePath, $key); } /** * COS 对象上传(Sign Algorithm=sha1) */ private function uploadFile($filePath, string $key): bool|array { $secretId = (string) ($this->config['access_key'] ?? ''); $secretKey = (string) ($this->config['secret_key'] ?? ''); $bucket = (string) ($this->config['bucket'] ?? ''); $region = (string) ($this->config['region'] ?? ''); $domain = rtrim((string) ($this->config['domain'] ?? ''), '/'); if ($secretId === '' || $secretKey === '' || $bucket === '' || $region === '') { UtilsService::getInstance()->errorThrow('腾讯云 COS 配置不完整(需要 SecretId/SecretKey/Bucket/Region)'); } $prefix = trim((string) ($this->config['path_prefix'] ?? ''), '/'); if ($prefix !== '' && !str_starts_with($key, $prefix . '/')) { $key = $prefix . '/' . ltrim($key, '/'); } $path = is_object($filePath) && method_exists($filePath, 'getRealPath') ? $filePath->getRealPath() : (string) $filePath; $content = file_get_contents($path); $host = $bucket . '.cos.' . $region . '.myqcloud.com'; $urlPath = '/' . ltrim($key, '/'); $now = time(); $keyTime = $now . ';' . ($now + 600); $signKey = hash_hmac('sha1', $keyTime, $secretKey); $httpString = strtolower('put') . "\n" . $urlPath . "\n\nhost=" . strtolower($host) . "\n"; $stringToSign = "sha1\n{$keyTime}\n" . sha1($httpString) . "\n"; $signature = hash_hmac('sha1', $stringToSign, $signKey); $authorization = 'q-sign-algorithm=sha1' . '&q-ak=' . $secretId . '&q-sign-time=' . $keyTime . '&q-key-time=' . $keyTime . '&q-header-list=host' . '&q-url-param-list=' . '&q-signature=' . $signature; $url = 'https://' . $host . $urlPath; $response = Http::withHeaders([ 'Host' => $host, 'Authorization' => $authorization, 'Content-Type' => 'application/octet-stream', ])->withBody($content, 'application/octet-stream')->put($url); if (!$response->successful()) { UtilsService::getInstance()->errorThrow('腾讯云上传失败:' . $response->body()); } $publicUrl = $domain !== '' ? ($domain . $urlPath) : $url; return ['key' => $key, 'url' => $publicUrl]; } }