cloudflare R2 for discuz X5.0/x3.5(非插件)

2026-03-20/技术/lz1473 OLEOU/
此功能需要修改3个地方
1,下载文件: S3.php 源代码 (GitHub) 操作: 打开链接后,按下 Ctrl + S 保存文件,命名为 class_s3.php 。上传到 /source/class/ 目录下
2,修改source/class/discuz/discuz_ftp.php文件,记得备份。

X3.5
<?php
/**
 * [Discuz!] (C)2001-2099 Comsenz Inc.
 * Cloudflare R2 Adapter for Discuz! X3.5 (Final Clean Version)
 * Created by OLEOU
 */

if(!defined('IN_DISCUZ')) {
    exit('Access Denied');
}

class discuz_ftp {

    public $enabled = false;
    public $config = array();
    public $s3 = null;
    public static $obj = null;

    // --- Cloudflare R2 核心配置 ---
    private $accessKey = '改成你的';
    private $secretKey = '改成你的';
    private $bucket    = '改成你的';
    private $endpoint  = '改成你的.r2.cloudflarestorage.com';
    // ----------------------------

    public static function instance($config = array()) {
        if(empty(self::$obj)) {
            self::$obj = new self($config);
        }
        return self::$obj;
    }

    public function __construct($config = array()) {
        $this->config = $config;
        
        if(!class_exists('S3')) {
            $s3_file = DISCUZ_ROOT.'./source/class/class_s3.php';
            if(file_exists($s3_file)) {
                require_once $s3_file;
            }
        }

        if(class_exists('S3')) {
            // R2 必须使用 SSL 握手
            $this->s3 = new S3($this->accessKey, $this->secretKey, true, $this->endpoint);
            $this->enabled = true;
        }
    }

    public function connect() {
        return ($this->enabled && $this->s3) ? 1 : 0;
    }

    public function upload($source, $target) {
        if(!$this->enabled || !$this->s3) return false;

        // 路径清洗,确保存储路径规范
        $target = preg_replace('/^(./|/)/', '', $target);

        // 执行上传,不带 ACL 参数以适配 R2 权限体系
        if($this->s3->putObjectFile($source, $this->bucket, $target)) {
            return true;
        }
        return false;
    }

    public function delete($path) {
        if(!$this->enabled || !$this->s3) return false;
        $path = preg_replace('/^(./|/)/', '', $path);
        return $this->s3->deleteObject($this->bucket, $path);
    }

    // 接口兼容性占位方法
    public function mkdir($directory) { return true; }
    public function chdir($directory) { return true; }
    public function ftp_mkdir($directory) { return true; }
    public function ftp_chdir($directory) { return true; }
    public function ftp_site($cmd) { return true; }
    public function ftp_chmod($filename, $mode) { return true; }
    public function set_option($key, $value) { return true; }
    public function close() { return true; }
}
X5.0
<?php

/**
 * [Discuz!] (C)2001-2099 Discuz! Team
 * Optimized for Cloudflare R2 by OLEOU (for X5.0)
 */

if(!defined('IN_DISCUZ')) {
	exit('Access Denied');
}

// 保持 X5.0 新增的错误常量定义
if(!defined('FTP_ERR_SERVER_DISABLED')) {
	define('FTP_ERR_SERVER_DISABLED', -100);
	define('FTP_ERR_CONFIG_OFF', -101);
	define('FTP_ERR_CONNECT_TO_SERVER', -102);
	define('FTP_ERR_USER_NO_LOGGIN', -103);
	define('FTP_ERR_CHDIR', -104);
	define('FTP_ERR_MKDIR', -105);
	define('FTP_ERR_SOURCE_READ', -106);
	define('FTP_ERR_TARGET_WRITE', -107);
	define('FTP_ERR_OSS_CONNECT_ERROR', -200);
}

class discuz_ftp {

	public $enabled = false;
	public $config = [];
	public $s3 = null;
	public static $obj = null;

	// --- Cloudflare R2 核心配置 ---
	private $accessKey = '改成你的';
	private $secretKey = '改成你的';
	private $bucket    = '改成你的';
	private $endpoint  = '改成你的.r2.cloudflarestorage.com';
	// ----------------------------

	public static function &instance($config = []) {
		if(empty(self::$obj)) {
			self::$obj = new self($config);
		}
		return self::$obj;
	}

	function __construct($config = []) {
		$this->config = !$config ? getglobal('setting/ftp') : $config;
		
		// 自动加载 S3 类库
		if(!class_exists('S3')) {
			$s3_file = DISCUZ_ROOT.'./source/class/class_s3.php';
			if(file_exists($s3_file)) {
				require_once $s3_file;
			}
		}

		if(class_exists('S3')) {
			$this->s3 = new S3($this->accessKey, $this->secretKey, true, $this->endpoint);
			$this->enabled = true;
		}
	}

	function upload($source, $target) {
		if(!$this->enabled || !$this->s3) return 0;

		// 适配 X5.0 的路径清洗
		$target = preg_replace('/^(./|/)/', '', $target);

		// 执行上传到 R2
		if($this->s3->putObjectFile($source, $this->bucket, $target)) {
			return 1;
		}
		return 0;
	}

	function connect() {
		// 告诉 X5.0 连接永远成功,直接进入上传环节
		return $this->enabled ? 1 : 0;
	}

	// 适配 X5.0 的删除逻辑
	function ftp_delete($path) {
		if(!$this->enabled || !$this->s3) return false;
		$path = preg_replace('/^(./|/)/', '', $path);
		return $this->s3->deleteObject($this->bucket, $path);
	}

	// --- 以下为 X5.0 要求的兼容性空方法 ---
	function ftp_chdir($directory) { return 1; }
	function ftp_mkdir($directory) { return 1; }
	function ftp_chmod($filename, $mod = 0777) { return 1; }
	function ftp_pwd() { return '/'; }
	function ftp_close() { return 1; }
	function set_error($code = 0) { }
	function error() { return 0; }
	function clear($str) { return str_replace(["n", "r", '..'], '', $str); }
}


3,设置路径:后台 -> 全局 -> 上传设置 -> 远程附件。
强制参数:
启用远程附件:选 是。
FTP 服务器地址:随便填(如 127.0.0.1),但不能不填。
FTP 端口:默认 21。
FTP 帐号/密码:随便填,不为空即可。
被动模式:选 是。
远程访问 URL:填写你的 R2 自定义域名,如 https://img.xxxx.com。

结束:测试上线吧!
评论列表
正在加载评论…
邮箱
验证码