CosService.php 12.2 KB
<?php

namespace App\Services;

use App\Exceptions\InquiryFilterException;
use App\Models\File\ImageSetting;
use App\Utils\LogUtils;
use Illuminate\Support\Str;
use Qcloud\Cos\Client;
/**
 * @remark :对象存储cos
 * @class  :CosService.php
 * @author :lyh
 * @time   :2023/7/19 15:09
 */
class CosService
{

    /**
     * @param $file
     * @remark :上传图片
     * @name   :uploadFile
     * @author :lyh
     * @method :post
     * @time   :2023/7/19 15:28
     */
    public function uploadFile(&$files,$path,$filename, $binary = false,$watermarkOptions = '')
    {
        $cos = config('filesystems.disks.cos');
        $cosClient = new Client([
            'region' => $cos['region'],
            'credentials' => [
                'secretId' => $cos['credentials']['secretId'],
                'secretKey' => $cos['credentials']['secretKey'],
            ],
        ]);
        $key = $path.'/'.$filename;
        // 判断是否为 Base64 编码的图片流文件
        if (Str::startsWith($files, 'data:image')) {
            // 分离 Base64 头部和数据部分
            [$meta, $base64Data] = explode(',', $files);
            // 解码 Base64 数据
            $Body = base64_decode($base64Data);
            if ($Body === false) {
                throw new \Exception("Base64 数据解码失败");
            }
        } else {
            // 如果不是 Base64 流文件,处理为普通文件上传
            $Body = $binary ? $files : fopen($files->getRealPath(), 'r');
        }
        $options = [
            'Bucket' => $cos['bucket'],
            'Key' => $key,
            'Body' => $Body,
        ];
        //水印
        if ($watermarkOptions) {
            $options['PicOperations'] = json_encode([
                'is_pic_info' => 1,
                'rules' => [
                    [
                        'fileid' => $key, // 使用相同的文件名保存
                        'rule' => $watermarkOptions,
                    ]
                ]
            ], true);
        }
        $res = $cosClient->putObject($options);
        return $key;
    }

    /**
     * @param $image_name
     * @remark :获取图片访问链接
     * @name   :getImageUrl
     * @author :lyh
     * @method :post
     * @time   :2023/7/19 16:08
     */
    public function getImageUrl($image_name)
    {
        $cos = config('filesystems.disks.cos');
        $cosClient = new Client([
            'region' => $cos['region'],
            'credentials' => [
                'secretId' => $cos['credentials']['secretId'],
                'secretKey' => $cos['credentials']['secretKey'],
            ],
        ]);
        $imageUrl = $cosClient->getObjectUrl($cos['bucket'], trim($image_name,'/'), '+10 years');
        return $imageUrl;
    }


    /**
     * 根据远程图片地址上传
     * @param $project_id
     * @param $image_type
     * @param $file_url
     * @param $key
     * @param $body_str
     * @param int $same_name 是否保持名称一直
     * @return string
     * @author Akun
     * @date 2023/09/21 9:39
     */
    public static function uploadRemote($project_id,$image_type,$file_url,$key='',$body_str='',$same_name=0)
    {
        if(!$key){
            $url_arr = parse_url($file_url);

            if($same_name){
                $path_arr = explode('/',$url_arr['path']);
                $filename = end($path_arr);
            }else{
                $ext = explode('.',$url_arr['path']);

                $filename = uniqid().rand(10000,99999).'.'.end($ext);
            }

            $uploads = config('upload.default_file');
            $path = $uploads['path_b'].'/'.$project_id.'/'.$image_type.'/'.date('Y-m');
            $key = $path.'/'.$filename;
        }

        $cos = config('filesystems.disks.cos');
        $cosClient = new Client([
            'region' => $cos['region'],
            'credentials' => [
                'secretId' => $cos['credentials']['secretId'],
                'secretKey' => $cos['credentials']['secretKey'],
            ],
        ]);

        if(empty($body_str)){
            try {
                $body_str = curl_c($file_url,false);
                if(!$body_str){
                    $body_str = file_get_contents($file_url);
                }
            }catch (\Exception $e){
                $body_str = '';
            }
        }

        if(!$body_str){
            return '';
        }

        try {
            $cosClient->putObject([
                'Bucket' => $cos['bucket'],
                'Key' => $key,
                'Body' => $body_str,
            ]);
            return $key;
        }catch (\Exception $e){
            LogUtils::error('uploadRemote error', $e->getMessage());
            return '';
        }
    }

    /**
     * @param $file
     * @return array
     * @throws \Exception
     * @author zbj
     * @date 2023/12/12
     */
    public function checkInquiryFile($file){
        $size = $file->getSize();
        if($size/1024/1024 > 20){
            throw new InquiryFilterException('Your file size exceeds the limit. Please upload a file no larger than 20MB.');
        }
        $extension = $file->getClientOriginalExtension();
//        JPEG (JPG)  PDF  DWG  STEP(STP)IGS word xlsx
        if(!in_array(strtolower($extension), ['png','jpg','jpeg', 'pdf', 'dwg', 'step', 'stp', 'igs','doc','docx','xls','xlsx'])){
            throw new InquiryFilterException('Please upload file in png, jpg, jpeg, pdf, dwg, step, stp, igs, doc, docx, xls or xlsx format.');
        }
        return [
            'size' => $size,
            'extension' => $extension,
            'name' =>  $file->getClientOriginalName(),
            'mime' =>  $file->getMimeType(),
        ];
    }


    /**
     * @remark :生成带水印的图片文件
     * @name   :addFieldImage
     * @author :lyh
     * @method :post
     * @time   :2024/8/19 11:01
     *   gravity/SouthEast:
            gravity:表示水印的对齐方式。常见的值有:
            NorthWest:左上角
            North:顶部中间
            NorthEast:右上角
            West:左侧中间
            Center:中心
            East:右侧中间
            SouthWest:左下角
            South:底部中间
            SouthEast:右下角
     */
    public function setWatermark($cdnUrl = '',$data = [],$is_image = false){
        $domain = 'http://globalso-v6-1309677403.cos.ap-hongkong.myqcloud.com';//cos域名
        $url = $domain . $cdnUrl;
        if($is_image){
            $param = [
                'image/'.urlSafeBase64Encode($domain.$data['image'] ?? ''),//图片
                'gravity/'.($data['gravity'] ?? 'southeast'),
                'dx/'.($data['dx'] ?? 0),
                'dy/'. ($data['dy'] ?? 0),
                'batch/'.($data['batch'] ?? 0),//平铺水印功能
                'dissolve/'.($data['dissolve'] ?? 50),//透明度
                'degree/'.($data['degree'] ?? 0),//旋转角度设置,取值范围为0 - 360,默认0
            ];
            $url = $url.'?watermark/1/'.implode('/',$param);
        }else{
            $param = [
                'text/'.urlSafeBase64Encode($data['text'] ?? ''),//文字水印名称
                'gravity/'.($data['gravity'] ?? 'southeast'),
                'dx/'.($data['dx'] ?? 10),
                'dy/'. ($data['dy'] ?? 10),
                'font/'.urlSafeBase64Encode($data['font'] ?? 'tahoma.ttf'),//默认宋体
                'fontsize/'.($data['fontsize'] ?? 24),//水印文字字体大小,单位为磅,缺省值13
                'fill/'.urlSafeBase64Encode($data['fill'] ?? '#3D3D3D'),//颜色
                'dissolve/'.($data['dissolve'] ?? 50),//透明度
                'degree/'.($data['degree'] ?? 0),//文字水印的旋转角度设置,取值范围为0 - 360,默认0
                'batch/'.($data['batch'] ?? 0),//平铺水印功能
                'shadow/'.($data['shadow'] ?? 0),//文字阴影效果,有效值为[0,100],默认为0,表示无阴影
            ];
            $url = $url.'?watermark/2/'.implode('/',$param);
        }
        return $url;
    }



    /**
     * @remark :添加水印后保存图片(覆盖/非覆盖的文件未存入数据库)
     * @name   :uploadImages
     * @author :lyh
     * @method :post
     * @time   :2024/8/19 17:06
     */
    public function coverOriginalImage($url,$cdnUrl){
        // 获取水印后的图片内容
        $imageContent = file_get_contents($url);
        // 使用 COS SDK 将图片重新上传并覆盖原图
        $cos = config('filesystems.disks.cos');
        $cosClient = new Client([
            'region' => $cos['region'],
            'credentials' => [
                'secretId' => $cos['credentials']['secretId'],
                'secretKey' => $cos['credentials']['secretKey'],
            ],
        ]);
        // 上传并覆盖原图
        $cosClient->putObject([
            'Bucket' => $cos['bucket'],
            'Key' => $cdnUrl, // 去掉域名部分,得到存储桶内的路径
            'Body' => $imageContent,
        ]);
        return $cos['cdn'].$cdnUrl;
    }

    /**
     * @remark :处理9宫格数据
     * @name   :getPosition
     * @author :lyh
     * @method :post
     * @time   :2024/8/19 15:16
     */
    public function getPosition(){
        return [
            1=>'northWest',
            2=>'north',
            3=>'northEast',
            4=>'west',
            5=>'center',
            6=>'east',
            7=>'southWest',
            8=>'south',
            9=>'southEast',
        ];
    }

    /**
     * @remark :字体
     * @name   :getFont
     * @author :lyh
     * @method :post
     * @time   :2024/8/19 15:47
     */
    public function getFont(){
        return [
            'simfang仿宋.ttf',
            'tahoma.ttf',
            'simhei黑体.ttf',
            'simkai楷体.ttf',
            'simsun宋体.ttc',
            'STHeiti Light华文黑体.ttc',
            'STHeiti Medium.ttc',
            '幼圆.TTF',
            'ahronbd.ttf',
            'arial.ttf',
            'ariblk.ttf',
            'Helvetica.dfont',
            'HelveticaNeue.dfont'
        ];
    }

    /**
     * @remark :获取cos图片高度
     * @name   :getImageHeight
     * @author :lyh
     * @method :post
     * @time   :2025/5/8 10:58
     * @param  :pathUrl->存储桶相对路径
     */
    public function getImageHeight($pathUrl){
        $cos = config('filesystems.disks.cos');
        $url = 'https://' . $cos['bucket'] . '.cos.' . $cos['region'] . '.myqcloud.com/' . ltrim($pathUrl, '/') . '?image/info';
        $imageInfo = @getimagesize($url);
        if ($imageInfo) {
//            $width = $imageInfo[0];
            $height = $imageInfo[1];
            return $height;
        }
        return '';
    }

    /**
     * @remark :裁剪图片
     * @name   :cropCosImage
     * @author :lyh
     * @method :post
     * @time   :2025/5/8 11:06
     */
    public function cropCosImage($cosUrl,$height = 220)
    {
        $cos = config('filesystems.disks.cos');
        $cosClient = new Client([
            'region' => $cos['region'],
            'credentials' => [
                'secretId' => $cos['credentials']['secretId'],
                'secretKey' => $cos['credentials']['secretKey'],
            ],
        ]);
        $pathInfo = pathinfo($cosUrl);
        $newKey = $pathInfo['dirname'] . '/crop_' . $pathInfo['filename'] .'.'. $pathInfo['extension'];
        $operations = [
            'is_pic_info' => 0,
            'rules' => [
                [
                    // 注意 fileid 要 base64 编码,并与 Key 相同才能覆盖
                    'fileid' => $newKey,
                    'rule' => 'imageMogr2/crop/x'.$height.'/gravity/center'
                ]
            ]
        ];
        // 执行裁剪并覆盖
        $res = $cosClient->ImageProcess([
            'Bucket' => $cos['bucket'],
            'Key' => $cosUrl, // 要处理的对象路径
            'PicOperations' => json_encode($operations),
        ]);
        if($res){
            return [
                'path' => $res['ProcessResults']['Object'][0]['Key'] ?? '',
                'size' => (int)$res['ProcessResults']['Object'][0]['Size'] ?? 0,
                'mime' => 'image/'.($res['ProcessResults']['Object'][0]['Format'] ?? 'jpg'),
                'type' => $res['ProcessResults']['Object'][0]['Format'] ?? 'jpg',
            ];
        }
        return [];
    }
}