|
@@ -465,8 +465,8 @@ class AIImageGenerationService
|
|
|
|
|
|
|
|
$pic_name = 'ai_generation_gpt_' . time() . '_' . uniqid() . '.png';
|
|
$pic_name = 'ai_generation_gpt_' . time() . '_' . uniqid() . '.png';
|
|
|
|
|
|
|
|
- // 压缩PNG图片
|
|
|
|
|
- $compressedData = compressRemoteImageUrlToSize($imageStream);
|
|
|
|
|
|
|
+ // 压缩PNG图片流
|
|
|
|
|
+ $compressedData = $this->compressImageStream($imageStream);
|
|
|
$url = uploadStreamByTos('image', $compressedData, $pic_name);
|
|
$url = uploadStreamByTos('image', $compressedData, $pic_name);
|
|
|
|
|
|
|
|
$result_urls[] = $url;
|
|
$result_urls[] = $url;
|
|
@@ -1621,4 +1621,110 @@ class AIImageGenerationService
|
|
|
|
|
|
|
|
return 'auto';
|
|
return 'auto';
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 压缩图片流(针对PNG格式)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param string $imageStream 图片二进制数据
|
|
|
|
|
+ * @param int $maxBytes 最大字节数(默认3MB)
|
|
|
|
|
+ * @return string|null 压缩后的图片二进制数据,失败时返回原数据
|
|
|
|
|
+ */
|
|
|
|
|
+ private function compressImageStream(string $imageStream, int $maxBytes = 3 * 1024 * 1024): ?string
|
|
|
|
|
+ {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 检查图片类型
|
|
|
|
|
+ $imgInfo = @getimagesizefromstring($imageStream);
|
|
|
|
|
+ if (!$imgInfo) {
|
|
|
|
|
+ dLog('generate')->warning('无法识别图片类型,返回原始数据');
|
|
|
|
|
+ return $imageStream;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $mime = $imgInfo['mime'] ?? '';
|
|
|
|
|
+
|
|
|
|
|
+ // 只处理PNG,JPEG不做有损压缩
|
|
|
|
|
+ if ($mime === 'image/jpeg') {
|
|
|
|
|
+ return $imageStream;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果已经小于最大限制,直接返回
|
|
|
|
|
+ if (strlen($imageStream) <= $maxBytes) {
|
|
|
|
|
+ return $imageStream;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 载入图像对象
|
|
|
|
|
+ $srcImg = @imagecreatefromstring($imageStream);
|
|
|
|
|
+ if (!$srcImg) {
|
|
|
|
|
+ dLog('generate')->warning('无法创建图像资源,返回原始数据');
|
|
|
|
|
+ return $imageStream;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $origW = imagesx($srcImg);
|
|
|
|
|
+ $origH = imagesy($srcImg);
|
|
|
|
|
+
|
|
|
|
|
+ // 逐步降低质量直到满足大小要求
|
|
|
|
|
+ $quality = 9; // PNG压缩等级 0-9
|
|
|
|
|
+ $scaleFactor = 1.0;
|
|
|
|
|
+ $maxAttempts = 10;
|
|
|
|
|
+ $attempt = 0;
|
|
|
|
|
+ $compressedData = null;
|
|
|
|
|
+
|
|
|
|
|
+ while ($attempt < $maxAttempts) {
|
|
|
|
|
+ $attempt++;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果需要缩放
|
|
|
|
|
+ if ($scaleFactor < 1.0) {
|
|
|
|
|
+ $newW = (int)($origW * $scaleFactor);
|
|
|
|
|
+ $newH = (int)($origH * $scaleFactor);
|
|
|
|
|
+ $resizedImg = imagecreatetruecolor($newW, $newH);
|
|
|
|
|
+
|
|
|
|
|
+ // 保持透明度
|
|
|
|
|
+ imagealphablending($resizedImg, false);
|
|
|
|
|
+ imagesavealpha($resizedImg, true);
|
|
|
|
|
+
|
|
|
|
|
+ imagecopyresampled($resizedImg, $srcImg, 0, 0, 0, 0, $newW, $newH, $origW, $origH);
|
|
|
|
|
+ $targetImg = $resizedImg;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $targetImg = $srcImg;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 输出到内存
|
|
|
|
|
+ ob_start();
|
|
|
|
|
+ imagepng($targetImg, null, $quality);
|
|
|
|
|
+ $compressedData = ob_get_clean();
|
|
|
|
|
+
|
|
|
|
|
+ if ($scaleFactor < 1.0) {
|
|
|
|
|
+ imagedestroy($targetImg);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查大小
|
|
|
|
|
+ $size = strlen($compressedData);
|
|
|
|
|
+ dLog('generate')->info('图片压缩尝试', [
|
|
|
|
|
+ 'attempt' => $attempt,
|
|
|
|
|
+ 'quality' => $quality,
|
|
|
|
|
+ 'scale' => $scaleFactor,
|
|
|
|
|
+ 'size' => $size,
|
|
|
|
|
+ 'target' => $maxBytes
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ if ($size <= $maxBytes) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 调整参数
|
|
|
|
|
+ if ($quality > 0) {
|
|
|
|
|
+ $quality--;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $scaleFactor *= 0.9; // 每次缩小10%
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ imagedestroy($srcImg);
|
|
|
|
|
+
|
|
|
|
|
+ return $compressedData ?: $imageStream;
|
|
|
|
|
+
|
|
|
|
|
+ } catch (\Exception $e) {
|
|
|
|
|
+ dLog('generate')->error('图片压缩失败', ['error' => $e->getMessage()]);
|
|
|
|
|
+ return $imageStream;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|