diff --git a/app/Http/Controllers/Api/FileController.php b/app/Http/Controllers/Api/FileController.php index 7dba36040..4d7b63abf 100755 --- a/app/Http/Controllers/Api/FileController.php +++ b/app/Http/Controllers/Api/FileController.php @@ -553,14 +553,14 @@ class FileController extends AbstractController $tmpPath = "uploads/file/document/" . date("Ym") . "/" . $id . "/attached/"; Base::makeDir(public_path($tmpPath)); $tmpPath .= md5($text) . "." . $matchs[1][$key]; - if (Base::saveContentImage(public_path($tmpPath), base64_decode($text))) { + if (Base::saveContentImage(public_path($tmpPath), base64_decode($text), 90)) { $paramet = getimagesize(public_path($tmpPath)); $data['content'] = str_replace($matchs[0][$key], ' $image64, "path" => $path, "fileName" => $fileName, - "scale" => $scale + "scale" => $scale, + "quality" => 85 ]); } else { $data = Base::upload([ @@ -892,7 +893,8 @@ class SystemController extends AbstractController "type" => 'image', "path" => $path, "fileName" => $fileName, - "scale" => $scale + "scale" => $scale, + "quality" => 100 ]); } if (Base::isError($data)) { @@ -1028,6 +1030,7 @@ class SystemController extends AbstractController "image64" => $image64, "path" => $path, "fileName" => $fileName, + "quality" => 85 ]); } else { $data = Base::upload([ @@ -1035,6 +1038,7 @@ class SystemController extends AbstractController "type" => 'file', "path" => $path, "fileName" => $fileName, + "quality" => 100 ]); } // diff --git a/app/Http/Controllers/IndexController.php b/app/Http/Controllers/IndexController.php index d18b7d7ed..f9e479c11 100755 --- a/app/Http/Controllers/IndexController.php +++ b/app/Http/Controllers/IndexController.php @@ -197,7 +197,8 @@ class IndexController extends InvokeController "file" => Request::file('file'), "type" => 'publish', "path" => $publishPath, - "fileName" => true + "fileName" => true, + "quality" => 100 ]); if (Base::isSuccess($res)) { file_put_contents($latestFile, $publishVersion); diff --git a/app/Ldap/LdapUser.php b/app/Ldap/LdapUser.php index ba0d1e251..6ca3d3b3d 100644 --- a/app/Ldap/LdapUser.php +++ b/app/Ldap/LdapUser.php @@ -146,7 +146,7 @@ class LdapUser extends Model $path = "uploads/user/ldap/"; $file = "{$path}{$user->userid}.jpeg"; Base::makeDir(public_path($path)); - if (Base::saveContentImage(public_path($file), $userimg)) { + if (Base::saveContentImage(public_path($file), $userimg, 90)) { $user->userimg = $file; } } diff --git a/app/Models/File.php b/app/Models/File.php index 2a2cdba7c..eb3a3b3d3 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -245,14 +245,13 @@ class File extends AbstractModel } } // - $setting = Base::setting('system'); $path = 'uploads/tmp/' . date("Ym") . '/'; $data = Base::upload([ "file" => Request::file('files'), "type" => 'more', "autoThumb" => false, "path" => $path, - "size" => ($setting['file_upload_limit'] ?: 0) * 1024 + "quality" => 100 ]); if (Base::isError($data)) { throw new ApiException($data['msg']); diff --git a/app/Models/ProjectTaskContent.php b/app/Models/ProjectTaskContent.php index f04e6c4b9..125e54474 100644 --- a/app/Models/ProjectTaskContent.php +++ b/app/Models/ProjectTaskContent.php @@ -79,7 +79,7 @@ class ProjectTaskContent extends AbstractModel $tmpPath = $path . 'attached/'; Base::makeDir(public_path($tmpPath)); $tmpPath .= md5($text) . "." . $matchs[1][$key]; - if (Base::saveContentImage(public_path($tmpPath), base64_decode($text))) { + if (Base::saveContentImage(public_path($tmpPath), base64_decode($text), 90)) { $paramet = getimagesize(public_path($tmpPath)); $content = str_replace($matchs[0][$key], ' $image64, "path" => $path, "fileName" => $fileName, + "quality" => 85 ]); } else if ($filePath) { Base::makeDir(public_path($path)); copy($filePath, public_path($path) . basename($filePath)); } else { - $setting = Base::setting('system'); $data = Base::upload([ "file" => $files, "type" => 'more', "path" => $path, "fileName" => $fileName, - "size" => ($setting['file_upload_limit'] ?: 0) * 1024, + "quality" => 100, "convertVideo" => true ]); } diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php index a087e3554..bb374ee28 100644 --- a/app/Models/WebSocketDialogMsg.php +++ b/app/Models/WebSocketDialogMsg.php @@ -703,9 +703,9 @@ class WebSocketDialogMsg extends AbstractModel $imagePath = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/"; Base::makeDir(public_path($imagePath)); $imagePath .= md5s($base64) . "." . $matchs[1][$key]; - if (Base::saveContentImage(public_path($imagePath), base64_decode($base64))) { + if (Base::saveContentImage(public_path($imagePath), base64_decode($base64), 90)) { $imageSize = getimagesize(public_path($imagePath)); - if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0)) { + if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0, 80)) { $imagePath .= "_thumb.{$extension}"; } $text = str_replace($matchs[0][$key], "[:IMAGE:browse:{$imageSize[0]}:{$imageSize[1]}:{$imagePath}::]", $text); @@ -779,7 +779,7 @@ class WebSocketDialogMsg extends AbstractModel } if (file_exists(public_path($imagePath))) { $imageSize = getimagesize(public_path($imagePath)); - if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0)) { + if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0, 80)) { $imagePath .= "_thumb.{$extension}"; } $text = str_replace($matchs[0][$key], "[:IMAGE:browse:{$imageSize[0]}:{$imageSize[1]}:{$imagePath}::]", $text); @@ -787,9 +787,9 @@ class WebSocketDialogMsg extends AbstractModel $image = file_get_contents($str); if (empty($image)) { $text = str_replace($matchs[0][$key], "[:IMAGE:browse:90:90:images/other/imgerr.jpg::]", $text); - } else if (Base::saveContentImage(public_path($imagePath), $image)) { + } else if (Base::saveContentImage(public_path($imagePath), $image, 90)) { $imageSize = getimagesize(public_path($imagePath)); - if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0)) { + if ($extension = Image::thumbImage(public_path($imagePath), public_path($imagePath) . "_thumb.{*}", 320, 0, 80)) { $imagePath .= "_thumb.{$extension}"; } $text = str_replace($matchs[0][$key], "[:IMAGE:browse:{$imageSize[0]}:{$imageSize[1]}:{$imagePath}::]", $text); diff --git a/app/Module/Base.php b/app/Module/Base.php index 62f3dbcf3..de25685be 100755 --- a/app/Module/Base.php +++ b/app/Module/Base.php @@ -2126,7 +2126,7 @@ class Base /** * image64图片保存 - * @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], autoThumb=>false不要自动生成缩略图, 'compress'=>是否压缩图片(默认true) ] + * @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], autoThumb=>false不要自动生成缩略图, 'quality'=>压缩图片质量(默认:0不压缩) ] * @return array [name=>文件名, size=>文件大小(单位KB),file=>绝对地址, path=>相对地址, url=>全路径地址, ext=>文件后缀名] */ public static function image64save($param) @@ -2169,19 +2169,19 @@ class Base "height" => -1, //图片高度 "ext" => $extension, //文件后缀名 ]; - //图片尺寸 + // 图片尺寸 $paramet = getimagesize($array['file']); $array['width'] = $paramet[0]; $array['height'] = $paramet[1]; - //原图压缩 + // 原图裁剪 if ($param['scale'] && is_array($param['scale'])) { list($width, $height) = $param['scale']; if (($width > 0 && $array['width'] > $width) || ($height > 0 && $array['height'] > $height)) { $cut = ($width > 0 && $height > 0) ? 'cover' : 'percentage'; $cut = $param['scale'][2] ?? $cut; - //图片压缩 + // 图片裁剪 $tmpFile = $array['file'] . '_tmp.jpg'; - if (Image::thumbImage($array['file'], $tmpFile, $width, $height, $cut)) { + if (Image::thumbImage($array['file'], $tmpFile, $width, $height, 90, $cut)) { $tmpSize = filesize($tmpFile); if ($tmpSize > $fileSize) { @unlink($tmpFile); @@ -2190,11 +2190,11 @@ class Base rename($tmpFile, $array['file']); } } - //图片尺寸 + // 更新图片尺寸 $paramet = getimagesize($array['file']); $array['width'] = $paramet[0]; $array['height'] = $paramet[1]; - //重命名 + // 重命名 if ($scaleName) { $scaleName = str_replace(['{WIDTH}', '{HEIGHT}'], [$array['width'], $array['height']], $scaleName); if (rename($array['file'], Base::rightDelete($array['file'], $fileName) . $scaleName)) { @@ -2206,8 +2206,9 @@ class Base } } // 压缩图片 - if ($param['compress'] !== false) { - Image::compressImage($array['file']); + $quality = intval($param['quality']); + if ($quality > 0) { + Image::compressImage($array['file'], null, $quality); $array['size'] = Base::twoFloat(filesize($array['file']) / 1024, true); } //生成缩略图 @@ -2216,7 +2217,7 @@ class Base $param['autoThumb'] = false; } if ($param['autoThumb'] !== false) { - if ($extension = Image::thumbImage($array['file'], $array['file'] . "_thumb.{*}", 320, 0)) { + if ($extension = Image::thumbImage($array['file'], $array['file'] . "_thumb.{*}", 320, 0, 80)) { $array['thumb'] .= "_thumb.{$extension}"; } } @@ -2238,7 +2239,7 @@ class Base size=>限制大小KB, autoThumb=>false不要自动生成缩略图, chmod=>权限(默认0644), - compress=>是否压缩图片(默认true) , + quality=>压缩图片质量(默认:0不压缩), convertVideo=>转换视频格式(默认false) , ] * @return array [ @@ -2304,10 +2305,15 @@ class Base if ($type && !in_array($extension, $type)) { return Base::retError('文件格式错误,限制类型:' . implode(",", $type)); } + $limitSize = intval($param['size']); + if ($limitSize <= 0) { + $fileUploadLimit = intval(Base::settingFind('system', 'file_upload_limit', 0)); + $limitSize = $fileUploadLimit * 1024; + } try { $fileSize = $file->getSize(); - if ($param['size'] > 0 && $fileSize > $param['size'] * 1024) { - return Base::retError('文件大小超限,最大限制:' . $param['size'] . 'KB'); + if ($limitSize > 0 && $fileSize > $limitSize * 1024) { + return Base::retError('文件大小超限,最大限制:' . $limitSize . 'KB'); } } catch (\Throwable) { $fileSize = 0; @@ -2353,7 +2359,7 @@ class Base return Base::retError('上传失败'); } @chmod($array['file'], $chmod); - //iOS照片颠倒处理 + // iOS照片颠倒处理 if (in_array($array['ext'], ['jpg', 'jpeg']) && function_exists('exif_read_data')) { $data = imagecreatefromstring(file_get_contents($array['file'])); $exif = @exif_read_data($array['file']); @@ -2402,19 +2408,19 @@ class Base } } if (in_array($array['ext'], ['jpg', 'jpeg', 'webp', 'gif', 'png'])) { - //图片尺寸 + // 获取图片尺寸 $paramet = getimagesize($array['file']); $array['width'] = $paramet[0]; $array['height'] = $paramet[1]; - //原图压缩 + // 原图裁剪 if ($param['scale'] && is_array($param['scale'])) { list($width, $height) = $param['scale']; if (($width > 0 && $array['width'] > $width) || ($height > 0 && $array['height'] > $height)) { $cut = ($width > 0 && $height > 0) ? 'cover' : 'percentage'; $cut = $param['scale'][2] ?? $cut; - //图片压缩 + // 图片裁剪 $tmpFile = $array['file'] . '_tmp.jpg'; - if (Image::thumbImage($array['file'], $tmpFile, $width, $height, $cut)) { + if (Image::thumbImage($array['file'], $tmpFile, $width, $height, 90, $cut)) { $tmpSize = filesize($tmpFile); if ($tmpSize > $fileSize) { @unlink($tmpFile); @@ -2423,11 +2429,11 @@ class Base rename($tmpFile, $array['file']); } } - //图片尺寸 + // 更新图片尺寸 $paramet = getimagesize($array['file']); $array['width'] = $paramet[0]; $array['height'] = $paramet[1]; - //重命名 + // 重命名 if ($scaleName) { $scaleName = str_replace(['{WIDTH}', '{HEIGHT}'], [$array['width'], $array['height']], $scaleName); if (rename($array['file'], Base::rightDelete($array['file'], $fileName) . $scaleName)) { @@ -2438,21 +2444,22 @@ class Base } } } - //生成缩略图 + // 生成缩略图 $array['thumb'] = $array['path']; if ($array['ext'] === 'gif' && !isset($param['autoThumb'])) { $param['autoThumb'] = false; } if ($param['autoThumb'] !== false) { - if ($array['ext'] = Image::thumbImage($array['file'], $array['file'] . "_thumb.{*}", 320, 0)) { + if ($array['ext'] = Image::thumbImage($array['file'], $array['file'] . "_thumb.{*}", 320, 0, 80)) { $array['thumb'] .= "_thumb.{$array['ext']}"; } } $array['thumb'] = Base::fillUrl($array['thumb']); } // 压缩图片 - if ($param['compress'] !== false) { - Image::compressImage($array['file']); + $quality = intval($param['quality']); + if ($quality > 0) { + Image::compressImage($array['file'], null, $quality); $array['size'] = Base::twoFloat(filesize($array['file']) / 1024, true); } // @@ -3010,13 +3017,13 @@ class Base * 保存图片到文件(同时压缩) * @param $path * @param $content - * @param $compress + * @param int $quality 压缩图片质量(默认:0不压缩) * @return bool */ - public static function saveContentImage($path, $content, $compress = true) { + public static function saveContentImage($path, $content, int $quality = 0) { if (file_put_contents($path, $content)) { - if ($compress) { - Image::compressImage($path); + if ($quality > 0) { + Image::compressImage($path, null, $quality); } return true; } diff --git a/app/Module/Image.php b/app/Module/Image.php index 0da364002..a16af0a4c 100644 --- a/app/Module/Image.php +++ b/app/Module/Image.php @@ -171,10 +171,11 @@ class Image * @param string $savePath 保存路径 * @param int $width 宽度 * @param int $height 高度 + * @param int $quality 压缩质量(0-100), 0 为不压缩 * @param string $mode 模式(percentage|cover|contain) * @return string|null 成功返回图片后缀,失败返回 false */ - public static function thumbImage(string $imagePath, string $savePath, int $width, int $height, string $mode = 'percentage'): ?string + public static function thumbImage(string $imagePath, string $savePath, int $width, int $height, int $quality = 0, string $mode = 'percentage'): ?string { if (!file_exists($imagePath)) { return null; @@ -187,6 +188,9 @@ class Image $image = new Image($imagePath); $image->thumb($width, $height, $mode); $image->saveTo($savePath); + if ($quality > 0) { + Image::compressImage($savePath, null, $quality); + } return $extension; } catch (\ImagickException) { return null; @@ -194,14 +198,14 @@ class Image } /** - * 压缩图片 + * 压缩图片(如果压缩后的图片比原图还大那就直接使用原图) * @param string $imagePath 图片路径 * @param string|null $savePath 保存路径(默认覆盖原图) * @param int $quality 压缩质量(0-100) - * @param float $minSize 最小尺寸(单位:KB) + * @param float $minSize 最小尺寸,小于这个尺寸不压缩(单位:KB) * @return bool */ - public static function compressImage(string $imagePath, string $savePath = null, int $quality = 100, float $minSize = 10): bool + public static function compressImage(string $imagePath, string $savePath = null, int $quality = 100, float $minSize = 5): bool { if (Base::settingFind("system", "image_compress") === 'close') { return false; @@ -209,6 +213,7 @@ class Image if (!file_exists($imagePath)) { return false; } + $quality = min(max($quality, 1), 100); $imageSize = filesize($imagePath); if ($minSize > 0 && $imageSize < $minSize * 1024) { return false; @@ -217,10 +222,7 @@ class Image $savePath = $imagePath; } $tmpPath = $imagePath . '.compress.tmp'; - try { - $image = new Image($imagePath); - $image->compress($quality); - $image->saveTo($tmpPath); + if (self::compressAuto($imagePath, $tmpPath, $quality)) { if (filesize($tmpPath) >= $imageSize) { copy($imagePath, $savePath); unlink($tmpPath); @@ -228,6 +230,32 @@ class Image rename($tmpPath, $savePath); } return true; + } + return false; + } + + /** + * 自动压缩图片(仅限于compressImage方法使用) + * @param string $imagePath + * @param string $savePath + * @param int $quality + * @return bool + */ + private static function compressAuto(string $imagePath, string $savePath, int $quality = 100): bool + { + if (strtolower(pathinfo($imagePath, PATHINFO_EXTENSION)) === 'png') { + $minQuality = $quality - 20; + $compressedContent = shell_exec("pngquant --quality={$minQuality}-{$quality} --strip - < " . $imagePath); + if ($compressedContent) { + file_put_contents($savePath, $compressedContent); + return true; + } + } + try { + $image = new Image($imagePath); + $image->compress($quality); + $image->saveTo($savePath); + return true; } catch (\ImagickException) { return false; } diff --git a/docker-compose.yml b/docker-compose.yml index 804d9c616..ecf2ada90 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: php: container_name: "dootask-php-${APP_ID}" - image: "kuaifan/php:swoole-8.0.rc15" + image: "kuaifan/php:swoole-8.0.rc16" shm_size: "2gb" ulimits: core: