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); } /** * 使用 OSS V1 签名上传对象 */ private function uploadFile($filePath, string $key): bool|array { $accessKey = (string) ($this->config['access_key'] ?? ''); $secretKey = (string) ($this->config['secret_key'] ?? ''); $bucket = (string) ($this->config['bucket'] ?? ''); $endpoint = (string) ($this->config['endpoint'] ?? ''); $domain = rtrim((string) ($this->config['domain'] ?? ''), '/'); if ($accessKey === '' || $secretKey === '' || $bucket === '' || $endpoint === '') { UtilsService::getInstance()->errorThrow('阿里云 OSS 配置不完整(需要 AccessKey/Secret/Bucket/Endpoint)'); } $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); $contentType = 'application/octet-stream'; $date = gmdate('D, d M Y H:i:s \G\M\T'); $resource = '/' . $bucket . '/' . $key; $stringToSign = "PUT\n\n{$contentType}\n{$date}\n{$resource}"; $signature = base64_encode(hash_hmac('sha1', $stringToSign, $secretKey, true)); $host = preg_replace('#^https?://#', '', rtrim($endpoint, '/')); // 支持传入 oss-cn-xxx.aliyuncs.com 或带 bucket 的域名 if (!str_starts_with($host, $bucket . '.')) { $host = $bucket . '.' . $host; } $url = 'https://' . $host . '/' . $key; $response = Http::withHeaders([ 'Date' => $date, 'Content-Type' => $contentType, 'Authorization' => 'OSS ' . $accessKey . ':' . $signature, ])->withBody($content, $contentType)->put($url); if (!$response->successful()) { UtilsService::getInstance()->errorThrow('阿里云上传失败:' . $response->body()); } $publicUrl = $domain !== '' ? ($domain . '/' . $key) : $url; return ['key' => $key, 'url' => $publicUrl]; } }