ChunsunUploader.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. namespace App\Services\Storage;
  3. use Illuminate\Support\Facades\Http;
  4. use Illuminate\Support\Facades\Log;
  5. use Illuminate\Support\Facades\Storage;
  6. class ChunsunUploader
  7. {
  8. /**
  9. * 从 URL 上传图片到春笋云
  10. */
  11. public function uploadFromUrl(string $url): string
  12. {
  13. try {
  14. // 1. 下载图片
  15. $response = Http::timeout(30)->get($url);
  16. if (!$response->successful()) {
  17. throw new \Exception("Failed to download image from URL: {$url}");
  18. }
  19. $imageContent = $response->body();
  20. $mimeType = $response->header('Content-Type', 'image/jpeg');
  21. // 2. 创建临时文件
  22. $extension = $this->getExtensionFromMimeType($mimeType);
  23. $tempFileName = 'temp_' . uniqid() . '.' . $extension;
  24. $tempPath = sys_get_temp_dir() . '/' . $tempFileName;
  25. file_put_contents($tempPath, $imageContent);
  26. // 3. 上传到春笋云
  27. $uploadedUrl = $this->uploadToChunsun($tempPath, $tempFileName);
  28. // 4. 清理临时文件
  29. @unlink($tempPath);
  30. return $uploadedUrl;
  31. } catch (\Exception $e) {
  32. Log::error('Chunsun upload failed', [
  33. 'url' => $url,
  34. 'error' => $e->getMessage(),
  35. ]);
  36. throw $e;
  37. }
  38. }
  39. /**
  40. * 从本地文件上传图片到春笋云
  41. */
  42. public function uploadFromFile(string $localPath): string
  43. {
  44. try {
  45. if (!file_exists($localPath)) {
  46. throw new \Exception("Local file does not exist: {$localPath}");
  47. }
  48. $fileName = basename($localPath);
  49. $uploadedUrl = $this->uploadToChunsun($localPath, $fileName);
  50. return $uploadedUrl;
  51. } catch (\Exception $e) {
  52. Log::error('Chunsun upload from file failed', [
  53. 'path' => $localPath,
  54. 'error' => $e->getMessage(),
  55. ]);
  56. throw $e;
  57. }
  58. }
  59. /**
  60. * 上传到春笋云
  61. */
  62. private function uploadToChunsun(string $filePath, string $fileName): string
  63. {
  64. $uploadUrl = config('services.chunsun.upload_url', env('CHUNSUN_UPLOAD_URL', 'https://crmapi.dcjxb.yunzhixue.cn'));
  65. try {
  66. $response = Http::timeout(60)
  67. ->attach('file', file_get_contents($filePath), $fileName)
  68. ->post($uploadUrl);
  69. if (!$response->successful()) {
  70. Log::error('Chunsun upload HTTP error', [
  71. 'status' => $response->status(),
  72. 'body' => $response->body(),
  73. 'url' => $uploadUrl,
  74. ]);
  75. throw new \Exception("Chunsun upload failed with status: {$response->status()}");
  76. }
  77. $data = $response->json();
  78. if (!isset($data['code']) || $data['code'] !== 200) {
  79. Log::error('Chunsun upload response error', [
  80. 'response' => $data,
  81. ]);
  82. throw new \Exception("Chunsun upload failed: " . ($data['message'] ?? 'Unknown error'));
  83. }
  84. if (!isset($data['data']['url'])) {
  85. throw new \Exception('No URL in Chunsun upload response');
  86. }
  87. Log::info('Chunsun upload successful', [
  88. 'file' => $fileName,
  89. 'url' => $data['data']['url'],
  90. ]);
  91. return $data['data']['url'];
  92. } catch (\Exception $e) {
  93. Log::error('Chunsun upload exception', [
  94. 'file' => $fileName,
  95. 'error' => $e->getMessage(),
  96. ]);
  97. throw $e;
  98. }
  99. }
  100. /**
  101. * 批量上传图片数组
  102. */
  103. public function uploadImagesFromArray(array $imageUrls): array
  104. {
  105. $uploadedUrls = [];
  106. foreach ($imageUrls as $index => $url) {
  107. $url = (string) $url;
  108. if ($url === '' || !preg_match('#^https?://#i', $url)) {
  109. // 非 http(s) URL 不处理(例如 data:、相对路径、本地路径),直接保留
  110. $uploadedUrls[$index] = $url;
  111. continue;
  112. }
  113. try {
  114. $uploadedUrls[$index] = $this->uploadFromUrl($url);
  115. } catch (\Exception $e) {
  116. Log::warning('Failed to upload image', [
  117. 'index' => $index,
  118. 'url' => $url,
  119. 'error' => $e->getMessage(),
  120. ]);
  121. // 保留原 URL 作为降级方案
  122. $uploadedUrls[$index] = $url;
  123. }
  124. }
  125. return $uploadedUrls;
  126. }
  127. /**
  128. * 根据 MIME 类型获取文件扩展名
  129. */
  130. private function getExtensionFromMimeType(string $mimeType): string
  131. {
  132. $extensions = [
  133. 'image/jpeg' => 'jpg',
  134. 'image/jpg' => 'jpg',
  135. 'image/png' => 'png',
  136. 'image/gif' => 'gif',
  137. 'image/webp' => 'webp',
  138. 'image/svg+xml' => 'svg',
  139. ];
  140. return $extensions[$mimeType] ?? 'jpg';
  141. }
  142. }