$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/file/upload */ private function putChunsun(string $path, string $binary): ?string { Log::info('PdfStorageService: Chunsun上传开始', [ 'path' => $path, ]); $uploadUrl = env('CHUNSUN_UPLOAD_URL', 'https://crmapi.dcjxb.yunzhixue.cn/file/upload'); try { // 获取扩展名并决定 MIME $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION) ?: 'jpg'); $mimeMap = [ 'pdf' => 'application/pdf', 'md' => 'text/markdown', 'markdown' => 'text/markdown', 'txt' => 'text/plain', 'png' => 'image/png', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', ]; $mimeType = $mimeMap[$extension] ?? 'application/octet-stream'; $baseName = basename($path); // 发送POST请求上传内容 $response = Http::timeout(60) ->attach('file', $binary, $baseName, ['Content-Type' => $mimeType]) ->post($uploadUrl); 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']; Log::info('PdfStorageService: Chunsun上传成功', [ 'path' => $path, 'url' => $uploadedUrl, ]); return $uploadedUrl; } catch (\Throwable $e) { Log::error('PdfStorageService: Chunsun上传异常', [ 'error' => $e->getMessage(), 'path' => $path, ]); return null; } } }