CRMEB单商户,图片支持本地存储和云存储(可动态切换)

当前版本CRMEB-BZ v5.4.0(20240708)

本方案优势

  • 可随时开启关闭云存储
  • 无需替换数据库内图片路径
  • 通过中间件动态替换图片路径

数据库变更

php think migrate:create UpdateSystemAttachment

public function change()
{
        $table->addColumn('cloud_name', 'string', ['limit' => MysqlAdapter::TEXT_TINY, 'default' => '', 'comment' => '附件名称'])
        ->addColumn('cloud_att_dir', 'string', ['limit' => MysqlAdapter::TEXT_TINY, 'default' => '', 'comment' => '附件路径'])
        ->addColumn('cloud_satt_dir', 'string', ['limit' => MysqlAdapter::TEXT_TINY, 'default' => '', 'comment' => '压缩图片路径'])
        ->update();
}

代码变更

1. 控制器变更

\app\adminapi\controller\v1\setting\SystemStorage::uploadType 方法,修改为

    /**
     * 切换存储类型
     * @param SystemConfigServices $services
     * @param $type
     * @return mixed
     */
    public function uploadType(SystemConfigServices $services, $type)
    {
        $upload_type = sys_config('upload_type', SystemAttachment::UPLOAD_TYPE_LOCAL);
        if (SystemAttachment::UPLOAD_TYPE_LOCAL !== (int)$upload_type) {
            // david 2024年11月21日 09:31:58
            return response_json()->fail('禁止修改存储类型');
        }

        $status = $this->services->count(['type' => $type, 'status' => 1]);
        if (!$status && $type != 1) {
            return app('json')->success(400227);
        }
        $services->update('upload_type', ['value' => json_encode($type)], 'menu_name');
        \crmeb\services\CacheService::clear();
        if ($type != 1) {
            $msg = 400228;
        } else {
            $msg = 400229;
        }
        return app('json')->success($msg);
    }

2. 服务层变更

初始化上传对象

\app\services\other\UploadService::init 方法,修改为

    /**
     * @param null $type
     * @return Upload|mixed
     */
    public static function init($type = null)
    {
        if (is_null($type)) {
            $type = (int)sys_config('upload_type', 1);
        }
        if (isset(self::$upload['upload_' . $type])) {
            return self::$upload['upload_' . $type];
        }
        $type = (int)$type;
        $config = [];
        switch ($type) {
            case SystemAttachment::UPLOAD_TYPE_QINIU://七牛
                $config = [
                    'accessKey' => sys_config('qiniu_accessKey'),
                    'secretKey' => sys_config('qiniu_secretKey'),
                ];
                break;
            case SystemAttachment::UPLOAD_TYPE_ALI_OSS:// oss 阿里云
                $config = [
                    'accessKey' => sys_config('accessKey'),
                    'secretKey' => sys_config('secretKey'),
                ];
                break;
            case SystemAttachment::UPLOAD_TYPE_TENCENT_COS:// cos 腾讯云
                $config = [
                    'accessKey' => sys_config('tengxun_accessKey'),
                    'secretKey' => sys_config('tengxun_secretKey'),
                    'appid' => sys_config('tengxun_appid'),
                ];
                break;
            case SystemAttachment::UPLOAD_TYPE_JD://京东云
                $config = [
                    'accessKey' => sys_config('jd_accessKey'),
                    'secretKey' => sys_config('jd_secretKey'),
                    'storageRegion' => sys_config('jd_storageRegion'),
                ];
                break;
            case SystemAttachment::UPLOAD_TYPE_HUAWEI://华为云
                $config = [
                    'accessKey' => sys_config('hw_accessKey'),
                    'secretKey' => sys_config('hw_secretKey'),
                ];
                break;
            case SystemAttachment::UPLOAD_TYPE_TIANYI://天翼云
                $config = [
                    'accessKey' => sys_config('ty_accessKey'),
                    'secretKey' => sys_config('ty_secretKey'),
                ];
                break;
            case SystemAttachment::UPLOAD_TYPE_LOCAL:
                break;
            default:
                throw new UploadException(400733);
        }

        //除了本地存储其他都去获取配置信息
        if (SystemAttachment::UPLOAD_TYPE_LOCAL !== $type) {
            /** @var SystemStorageServices $make */
            $make = app()->make(SystemStorageServices::class);
            $res = $make->getConfig($type);
            $config['uploadUrl'] = $res['domain'];
            $config['storageName'] = $res['name'];
            if (5 !== $type) $config['storageRegion'] = $res['region'];
            $config['cdn'] = $res['cdn'];
        }

        $thumb = SystemConfigService::more(['thumb_big_height', 'thumb_big_width', 'thumb_mid_height', 'thumb_mid_width', 'thumb_small_height', 'thumb_small_width',]);
        $water = SystemConfigService::more([
            'image_watermark_status',
            'watermark_type',
            'watermark_image',
            'watermark_opacity',
            'watermark_position',
            'watermark_rotate',
            'watermark_text',
            'watermark_text_angle',
            'watermark_text_color',
            'watermark_text_size',
            'watermark_x',
            'watermark_y']);
        $config = array_merge($config, ['thumb' => $thumb], ['water' => $water]);
        return self::$upload['upload_' . $type] = new Upload($type, $config);
    }

图片上传

\app\services\system\attachment\SystemAttachmentServices::upload 方法,修改为

    /**
     * 图片上传
     * @param int $pid
     * @param string $file
     * @param int $upload_type
     * @param int $type
     * @param $menuName
     * @param string $uploadToken
     * @return mixed
     */
    public function upload(int $pid, string $file, int $upload_type, int $type, $menuName, $uploadToken = '')
    {
        $realName = false;
        if ($upload_type == 0) {
            $upload_type = sys_config('upload_type', 1);
        }
        if ($menuName == 'weixin_ckeck_file' || $menuName == 'ico_path') {
            $upload_type = 1;
            $realName = true;
        }
        try {
            $path = make_path('attach', 2, true);
            if ($path === '') {
                throw new AdminException(400555);
            }

            // 本地始终保留一份 david 2024年10月31日 10:44:15
            $upload = UploadService::init(1);
            $local = $upload->to($path)->validate()->move($file, $realName);
            if ($local === false) {
                throw new UploadException($upload->getError());
            } else {
                $fileInfo = $upload->getUploadInfo();
                $fileType = pathinfo($fileInfo['name'], PATHINFO_EXTENSION);
                if ($fileInfo && $type == 0 && !in_array($fileType, ['xlsx', 'xls', 'mp4'])) {
                    $data['name'] = $fileInfo['name'];
                    $data['real_name'] = $fileInfo['real_name'];
                    $data['att_dir'] = $fileInfo['dir'];
                    $data['satt_dir'] = $fileInfo['thumb_path'];
                    $data['att_size'] = $fileInfo['size'];
                    $data['att_type'] = $fileInfo['type'];
                    $data['image_type'] = 1;
                    $data['module_type'] = 1;
                    $data['time'] = $fileInfo['time'] ?? time();
                    $data['pid'] = $pid;
                    $data['scan_token'] = $uploadToken;
                    /** @var SystemAttachment $systemAttachment */
                    $systemAttachment = $this->dao->save($data);
                }
                if ($upload_type == 1) {
                    return $local->filePath;
                }
            }

            // 云存储
            $upload = UploadService::init($upload_type);
            $res = $upload->to(DS . 'uploads' . $path)->validate()->move($file, $realName);
            if ($res === false) {
                throw new UploadException($upload->getError());
            } else {
                $fileInfo = $upload->getUploadInfo();
                $fileType = pathinfo($fileInfo['name'], PATHINFO_EXTENSION);
                if ($fileInfo && $type == 0 && !in_array($fileType, ['xlsx', 'xls', 'mp4'])) {
                    $cloud = [];
                    // 保存云存储文件信息 david 2024年11月21日 13:07:52
                    $cloud['cloud_name'] = $fileInfo['name'];
                    $cloud['cloud_att_dir'] = $fileInfo['dir'];
                    $cloud['cloud_satt_dir'] = $fileInfo['thumb_path'];
                    $cloud['image_type'] = $upload_type;
                    if (isset($systemAttachment) && $systemAttachment instanceof SystemAttachment) {
                        $systemAttachment->save($cloud);
                    }
                }
                return $local->filePath;
            }
        } catch (Throwable $e) {
            throw new UploadException($e->getMessage());
        }
    }

3. 模型层变更

\app\model\system\attachment\SystemAttachment 增加属性和常量

    /**
     * 禁止写入时间戳
     * @var bool
     */
    protected $autoWriteTimestamp = false;

    /**
     * 本地存储
     */
    public const UPLOAD_TYPE_LOCAL = 1;
    /**
     * 七牛云存储
     */
    public const UPLOAD_TYPE_QINIU = 2;
    /**
     * 阿里云OSS
     */
    public const UPLOAD_TYPE_ALI_OSS = 3;
    /**
     * 腾讯云COS
     */
    public const UPLOAD_TYPE_TENCENT_COS = 4;
    /**
     * 京东云
     */
    public const UPLOAD_TYPE_JD = 5;
    /**
     * 华为云
     */
    public const UPLOAD_TYPE_HUAWEI = 6;
    /**
     * 天翼云
     */
    public const UPLOAD_TYPE_TIANYI = 7;

4. 新增中间件

app/middleware/UploadsUrlReplace.php 内容为

<?php
declare (strict_types=1);

namespace app\middleware;

use app\model\system\attachment\SystemAttachment;
use app\Request;
use Closure;
use think\facade\Config;
use think\Response;

/**
 * 文件路径替换中间件
 */
class UploadsUrlReplace
{
    /**
     * 处理的路由前缀
     */
    private const HOOK_ROUTE_PREFIX = [
        '/api/'
    ];

    /**
     * 处理请求
     * @param Request $request
     * @param Closure $next
     * @return Response
     */
    public function handle(Request $request, Closure $next): Response
    {
        $baseUrl = $request->baseUrl();
        $isSkip = true;
        foreach (self::HOOK_ROUTE_PREFIX as $prefix) {
            if (0 === strpos($baseUrl, $prefix)) {
                $isSkip = false;
                break;
            }
        }

        $upload_type = sys_config('upload_type', 1);
        /** @var Response $response */
        $response = $next($request);
        if ($isSkip || SystemAttachment::UPLOAD_TYPE_LOCAL === (int)$upload_type) {
            return $response;
        }

        $site_url = sys_config('site_url');
        $host = parse_url($site_url, PHP_URL_HOST);
        $cdnBaseUrl = parse_url(Config::get('upload.cdn_base_url'), PHP_URL_HOST);
        $pattern = '/' . $host . '.+\.(jpg|jpeg|png|gif|svg)"/i';
        $content = $response->getContent();
        if (false !== preg_match($pattern, $content, $matches)) {
            $content = preg_replace_callback($pattern, fn($match) => str_replace($host, $cdnBaseUrl, $match[0]), $content);
            $response->content($content);
        }

        return $response;
    }
}

5. 配置变更

config/upload.php 新增配置项

    // CDN或对象存储的完整域名
    'cdn_base_url' => 'CDN或对象存储的完整域名',

app/middleware.php 全局中间件变更

启用中间件

    // 文件路径替换中间件
    \app\middleware\UploadsUrlReplace::class,
最后修改:2024 年 11 月 22 日 01 : 24 PM
如果觉得我的文章对你有用,请随意赞赏

发表评论