$this->putUpyun($path, $binary), 'chunsun' => $this->putChunsun($path, $binary), default => $this->putLocal($path, $binary), }; } private function putLocal(string $path, string $binary): ?string { $disk = 'public'; // 确保目录存在 $dir = Str::beforeLast($path, '/'); if ($dir && $dir !== $path) { Storage::disk($disk)->makeDirectory($dir); } $written = Storage::disk($disk)->put($path, $binary); if (!$written) { Log::error('PdfStorageService: 本地存储失败', ['path' => $path, 'disk' => $disk]); return null; } return url(Storage::url($path)); } /** * 又拍云存储(REST API),需要以下 env: * PDF_STORAGE_DRIVER=upyun * UPYUN_BUCKET=xxx * UPYUN_OPERATOR=xxx * UPYUN_PASSWORD=xxx * UPYUN_DOMAIN=https://your.cdn.domain 或 https://v0.api.upyun.com */ private function putUpyun(string $path, string $binary): ?string { $bucket = env('UPYUN_BUCKET'); $operator = env('UPYUN_OPERATOR'); $password = env('UPYUN_PASSWORD'); $domain = rtrim(env('UPYUN_DOMAIN', 'https://v0.api.upyun.com'), '/'); if (!$bucket || !$operator || !$password) { Log::error('PdfStorageService: 又拍云配置缺失', compact('bucket', 'operator')); return null; } $target = "{$domain}/{$bucket}/" . ltrim($path, '/'); try { $response = Http::withHeaders([ 'Content-Type' => 'application/pdf', 'Content-Length' => strlen($binary), ]) ->withBasicAuth($operator, $password) ->timeout(30) ->withBody($binary, 'application/pdf') ->put($target); if (!$response->successful()) { Log::error('PdfStorageService: 又拍云上传失败', [ 'status' => $response->status(), 'body' => $response->body(), 'target' => $target, ]); return null; } // 生成访问 URL,优先使用 CDN 域名 $cdn = env('UPYUN_CDN_DOMAIN'); if ($cdn) { return rtrim($cdn, '/') . '/' . ltrim($path, '/'); } return $target; } catch (\Throwable $e) { Log::error('PdfStorageService: 又拍云异常', [ 'error' => $e->getMessage(), 'target' => $target ?? null, ]); return null; } } /** * Chunsun云存储上传(POST multipart/form-data) * 环境变量: * PDF_STORAGE_DRIVER=chunsun * CHUNSUN_UPLOAD_URL=https://crmapi.dcjxb.yunzhixue.cn */ private function putChunsun(string $path, string $binary): ?string { $uploadUrl = env('CHUNSUN_UPLOAD_URL', 'https://crmapi.dcjxb.yunzhixue.cn'); try { // 创建临时文件 $tempFile = tempnam(sys_get_temp_dir(), 'chunsun_pdf_') . '.pdf'; file_put_contents($tempFile, $binary); // 发送POST请求上传文件 $response = Http::timeout(60) ->attach('file', file_get_contents($tempFile), basename($tempFile) . '.pdf') ->post($uploadUrl); // 清理临时文件 @unlink($tempFile); if (!$response->successful()) { Log::error('PdfStorageService: Chunsun上传失败', [ 'status' => $response->status(), 'body' => $response->body(), 'url' => $uploadUrl, ]); return null; } $data = $response->json(); // 检查响应格式 if (!isset($data['code']) || $data['code'] !== 200) { Log::error('PdfStorageService: Chunsun上传失败,响应格式错误', [ 'response' => $data, ]); return null; } // 检查返回数据 if (!isset($data['data']['url'])) { Log::error('PdfStorageService: Chunsun上传失败,未返回URL', [ 'response' => $data, ]); return null; } $uploadedUrl = $data['data']['url']; $uploadedName = $data['data']['name'] ?? null; Log::info('PdfStorageService: Chunsun上传成功', [ 'path' => $path, 'url' => $uploadedUrl, 'name' => $uploadedName, ]); return $uploadedUrl; } catch (\Throwable $e) { Log::error('PdfStorageService: Chunsun上传异常', [ 'error' => $e->getMessage(), 'path' => $path, 'trace' => $e->getTraceAsString(), ]); return null; } } }