news 2026/4/3 4:51:34

thinkcmf改存储CloudflareR2

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
thinkcmf改存储CloudflareR2

先建一个服务类

<?php namespace app\portal\service; class CloudflareR2 { private $accessKey="42098b06dbxxxxxxxx48fad96f4f"; private $secretKey="02d3f18c4ac204d35xxxxxxxxxxxx73bcab3e4c01c982b16fe56c7a7951"; private $accountId="e94bae5daxxxxxx3abbd269e5e"; private $bucketName="suxxxxxoda"; private $region="auto"; public function __construct() { } /** * 获取存储桶 URL */ public function getBucketUrl() { return "{$this->accountId}.r2.cloudflarestorage.com/{$this->bucketName}"; } /** * 上传文件到 Cloudflare R2 */ public function uploadFile($filePath, $objectKey) { if (!file_exists($filePath)) { return ['success' => false, 'error' => 'File not found']; } // 使用同一个时间戳确保所有日期一致 $now = time(); $amzDate = gmdate('Ymd\THis\Z', $now); $dateStamp = gmdate('Ymd', $now); $fileSize = filesize($filePath); $contentType = mime_content_type($filePath); $fileContent = file_get_contents($filePath); // 1. 创建规范请求 $canonicalHeaders = [ 'content-length' => $fileSize, 'content-type' => $contentType, 'host' => "{$this->accountId}.r2.cloudflarestorage.com", 'x-amz-content-sha256' => hash('sha256', $fileContent), 'x-amz-date' => $amzDate ]; // 按字母顺序排序头 ksort($canonicalHeaders); $signedHeaders = implode(';', array_map('strtolower', array_keys($canonicalHeaders))); $canonicalRequest = "PUT\n" . '/' . $this->bucketName . '/' . str_replace('%2F', '/', rawurlencode($objectKey)) . "\n" . "\n" . implode("\n", array_map(function($k, $v) { return strtolower($k) . ':' . $v; }, array_keys($canonicalHeaders), $canonicalHeaders)) . "\n" . "\n" . $signedHeaders . "\n" . $canonicalHeaders['x-amz-content-sha256']; // 2. 创建待签字符串 $algorithm = "AWS4-HMAC-SHA256"; $credentialScope = "{$dateStamp}/{$this->region}/s3/aws4_request"; $stringToSign = "{$algorithm}\n{$amzDate}\n{$credentialScope}\n" . hash('sha256', $canonicalRequest); // 3. 计算签名 $signingKey = $this->getSignatureKey($dateStamp); $signature = hash_hmac('sha256', $stringToSign, $signingKey); // 4. 构建授权头 $authorizationHeader = "{$algorithm} " . "Credential={$this->accessKey}/{$credentialScope}, " . "SignedHeaders={$signedHeaders}, " . "Signature={$signature}"; // 5. 构造请求URL和头 $url = "https://{$this->accountId}.r2.cloudflarestorage.com/{$this->bucketName}/" . rawurlencode($objectKey); $headers = [ "Authorization: {$authorizationHeader}", "Content-Length: {$fileSize}", "Content-Type: {$contentType}", "Host: {$this->accountId}.r2.cloudflarestorage.com", "x-amz-content-sha256: {$canonicalHeaders['x-amz-content-sha256']}", "x-amz-date: {$amzDate}" ]; // 6. 发送请求 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); curl_setopt($ch, CURLOPT_POSTFIELDS, $fileContent); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); //var_dump($response); die(); curl_close($ch); if ($httpCode === 200) { $url2 = env('CLOUDFLARE_URL') . rawurlencode($objectKey); return ['success' => true, 'url' => $url2]; } else { return [ 'success' => false, 'error' => 'Upload failed', 'http_code' => $httpCode, 'response' => $response, 'debug' => [ 'canonicalRequest' => $canonicalRequest, 'stringToSign' => $stringToSign, 'signingKey' => bin2hex($this->getSignatureKey($dateStamp)), 'signature' => $signature, 'headers' => $headers ] ]; } } /** * 生成签名密钥 */ private function getSignatureKey($dateStamp) { $kSecret = 'AWS4' . $this->secretKey; $kDate = hash_hmac('sha256', $dateStamp, $kSecret, true); $kRegion = hash_hmac('sha256', $this->region, $kDate, true); $kService = hash_hmac('sha256', 's3', $kRegion, true); $kSigning = hash_hmac('sha256', 'aws4_request', $kService, true); return $kSigning; } }

再来个调用的

<?php namespace app\portal\service; use cmf\lib\Storage; class CloudflareR2Storage extends Storage { /** * 上传文件到 Cloudflare R2 * @param string $file 文件路径 * @param string $filePath 本地文件路径 * @param string $fileType 文件类型 * @param array $param 其他参数 * @return array|bool */ public function upload($file, $filePath, $fileType = 'image', $param = null) { $cloudflareR2 = new CloudflareR2(); $objectKey = ltrim($file, '/'); $result = $cloudflareR2->uploadFile($filePath, $objectKey); if ($result['success']) { // 删除本地文件 // @unlink($filePath); return [ 'preview_url' => $result['url'], 'url' => $result['url'], ]; } else { return false; } } /** * 获取图片预览 URL * @param string $file * @param string $style * @return mixed */ public function getPreviewUrl($file, $style = '') { return "https://" . (new CloudflareR2())->getBucketUrl() . "/" . ltrim($file, '/'); } /** * 获取图片 URL * @param string $file * @param string $style * @return mixed */ public function getImageUrl($file, $style = '') { return "https://" . (new CloudflareR2())->getBucketUrl() . "/" . ltrim($file, '/'); } /** * 获取文件下载 URL * @param string $file * @param int $expires * @return mixed|string */ public function getFileDownloadUrl($file, $expires = 3600) { return "https://" . (new CloudflareR2())->getBucketUrl() . "/" . ltrim($file, '/'); } }

然后找到cmf的扩展,找到对应的上传方法

在他上传的方法前面插入个自己的上传判断

// 检查是否配置了 Cloudflare R2 存储 $storage = cmf_get_option('storage'); if ( env('CLOUDFLARE_STATUS') == 1) { // 使用 Cloudflare R2 存储 $cloudflareR2Storage = new \app\portal\service\CloudflareR2Storage(); $result = $cloudflareR2Storage->upload($arrInfo["file_path"], $uploadPath . $arrInfo["file_path"], $fileType); if ($result) { // 删除本地文件 // @unlink($uploadPath . $arrInfo["file_path"]); return array_merge([ 'filepath' => $arrInfo["file_path"], "name" => $arrInfo["filename"], 'id' => $strId, 'preview_url' => $result['preview_url'], 'url' => $result['url'], ], $result); } }

我是直接写在配置文件读取的,要不要写数据库看自己了.

这样就能实现上传到Cloundflare,接着还要处理回显的问题.找到这函数

,改写他

function cmf_get_image_url($file, $style = 'watermark') { if (empty($file)) { return ''; } if (strpos($file, "http") === 0) { return $file; } else { // 获取云存储配置 $CLOUDFLARE_STATUS = env("CLOUDFLARE_STATUS"); $cloudStorageUrl = env("CLOUDFLARE_URL"); // 如果配置了云存储URL,则直接使用 if ($CLOUDFLARE_STATUS == 1) { // 处理路径 $file = ltrim(str_replace('\\', '/', $file), '/'); //如果不是thems开头的需要加上upload前缀 // if (!preg_match('/^themes/', $file) && !preg_match('/^static/', $file)) { // $file = 'upload/' . $file; // } return rtrim($cloudStorageUrl, '/') . '/' . $file; } else { // 使用默认的存储处理 $storage = Storage::instance(); return $storage->getImageUrl($file, $style); } } }

具体匹配规则看自己实际目录结构了.

这样就完成了上传到Cloudflare.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/25 14:21:30

云南民宿联盟:破解诗与远方的生存密码

在社交平台上&#xff0c;一家家设计精美的民宿被赞为"诗与远方"的载体&#xff1b;但在经营者的账本上&#xff0c;这些美好却常伴随着现实的挑战——旺季一房难求的背后&#xff0c;是长达数月的淡季空置&#xff1b;独特的设计风格赢得口碑&#xff0c;却难掩品牌…

作者头像 李华
网站建设 2026/3/24 14:49:47

springboot的商城购物的网站_rfk3dw0e-

目录已开发项目效果实现截图开发技术系统开发工具&#xff1a;核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&…

作者头像 李华
网站建设 2026/3/31 17:51:21

SmartCrop.js 高效升级实战指南:从入门到精通的版本迁移技巧

SmartCrop.js 高效升级实战指南&#xff1a;从入门到精通的版本迁移技巧 【免费下载链接】smartcrop.js Content aware image cropping 项目地址: https://gitcode.com/gh_mirrors/smar/smartcrop.js SmartCrop.js 作为智能图像裁剪的领先工具&#xff0c;其 2.x 版本在…

作者头像 李华
网站建设 2026/4/2 13:59:04

Lazy.js实战指南:5个让数据处理性能翻倍的业务场景

Lazy.js实战指南&#xff1a;5个让数据处理性能翻倍的业务场景 【免费下载链接】lazy.js Like Underscore, but lazier 项目地址: https://gitcode.com/gh_mirrors/la/lazy.js 在当今数据驱动的业务环境中&#xff0c;高效处理大规模数据集已成为开发者的核心挑战。Lazy…

作者头像 李华
网站建设 2026/4/1 3:30:08

基于Spring Boot+MybatisPlus线上美食社区_17owxdug-java毕业设计

目录已开发项目效果实现截图开发技术系统开发工具&#xff1a;核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&…

作者头像 李华
网站建设 2026/3/28 12:08:37

ERNIE 4.5量化技术深度解析:开启大模型普惠应用新时代

ERNIE 4.5量化技术深度解析&#xff1a;开启大模型普惠应用新时代 【免费下载链接】ERNIE-4.5-300B-A47B-W4A8C8-TP4-Paddle 项目地址: https://ai.gitcode.com/hf_mirrors/baidu/ERNIE-4.5-300B-A47B-W4A8C8-TP4-Paddle ERNIE 4.5系列作为百度最新一代大语言模型&…

作者头像 李华