使用Cloudflare R2的三种方式——web、桌面端、springboot

3,004 阅读4分钟

一、Cloudflare R2 基础使用流程

1. 注册与服务开通详解

账号注册步骤:

  1. 访问 Cloudflare 官网 点击 "Sign Up"
  2. 在控制台左侧菜单找到 "R2 Storage"

服务激活要点:

  • 地域选择建议:初次使用推荐亚太区域,网络延迟较低
  • 存储类默认选择:Standard Storage(免费计划仅支持该类型)

2. 存储桶(Bucket)创建全流程

核心配置项说明:

配置项推荐设置说明
存储桶名称全局唯一小写字母组合例:my-photo-bucket-2025,后续可作为自定义域名前缀
区域选择离用户最近的 Cloudflare 节点自动 即可
存储类设置保持 Standard 不变

1747987920718.png

公共访问配置实操:

  1. 进入存储桶详情页,点击 "设置" 标签

3.png 2. 开启 "公共开发URL" 开关

  1. 上传测试文件后,在文件详情页获取形如https://<bucket>.r2.cloudflarestorage.com/<path>的 URL

4.png

4. 自定义域名绑定高级设置

配置优势:

  • 统一资源访问域名(如https://img.example.com
  • 利用 Cloudflare CDN 节点加速全球访问
  • 支持 HTTPS 免费证书自动部署

前提条件

域名需在cloudfare托管(修改原域名购买网站的dns解析服务器)

操作步骤

  1. 在存储桶设置中选择 "自定义域名"
  2. 输入已托管在 Cloudflare 的域名(如img.example.com

QQ20250523-162223.png 4. 系统自动生成 CNAME 记录,确认 DNS 解析生效 QQ20250523-162442.png

二、API令牌的获取

QQ20250523-163137.png

QQ20250523-163152.png

注意:该页面的密钥只展示一次,关闭后无法再次查看

QQ20250523-163305.png

三、桌面端工具 PicGo

1. PicGo 安装与环境配置

稳定版安装包获取:

关键插件安装:

  1. 打开 PicGo 设置中的 "插件设置",搜索s3并安装picgo-plugin-s3插件

QQ20250523-163747.png

2.重启 PicGo 使插件生效,根据提示填写 QQ20250523-163830.png

2. R2 存储配置参数详解

配置表单填写指南:

字段名称取值示例数据来源说明
存储区域auto保持默认即可,自动适配 Cloudflare 区域
存储桶名称my-photo-bucket-2025填写已创建的 R2 存储桶名称
访问密钥 IDABCDEFGHIJKLMNOPQRSTUVW从 Cloudflare 控制台 API 令牌页面获取
密钥访问密钥1234567890abcdefghijklmnopqrstuvw同上,注意该值仅显示一次需妥善保存
endpointhttps:// <accountID>.r2.cloudflarestorage.com格式固定,accountID 可在存储桶概览页获取
CDN 域名img.example.com填写已绑定的自定义域名

3. 高效上传操作技巧

QQ20250523-163958.png

支持的上传方式:

  • 拖拽上传:直接拖放图片到 PicGo 窗口
  • 剪贴板上传:截图后按Ctrl+Shift+P快捷上传
  • 批量上传:选择多个文件一次性导入
  • 插件联动:与 Markdown 编辑器集成,自动替换本地图片链接

三、Spring Boot 后端集成完整方案

1. 依赖管理与版本说明

Maven 依赖配置:

    <dependencies>
        <!-- AWS SDK for S3 (兼容Cloudflare R2) -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <version>2.29.52</version>
        </dependency>
        <!-- EC2依赖用于区域自动识别 -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>ec2</artifactId>
            <version>2.29.52</version>
        </dependency>
        <!-- HTTP客户端用于底层请求 -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
    </dependencies>

版本选择注意事项:

  • 2.30.0 + 版本需额外配置签名算法

QQ20250523-164347.png

2. 配置文件与常量管理

application.yml 完整配置:

    cloud:
      aws:
        s3:
          endpoint: https://abc123.r2.cloudflarestorage.com  # 替换为实际accountID
          bucket-name: my-spring-boot-bucket                # 替换为实际存储桶名
          cdn-domain: img.example.com                        # 替换为自定义域名
          region: auto                                       # 自动识别区域
          credentials:
            access-key: YOUR_ACCESS_KEY                      # 替换为API访问密钥
            secret-key: YOUR_SECRET_KEY                      # 替换为API秘密密钥

3. 核心代码实现

config代码

@Component
public class R2FileUtils implements InitializingBean {

    public static String END_POINT;
    public static String REGION;
    public static String BUCKET_NAME;
    public static String CDN_DOMAIN;
    public static String ACCESS_KEY;
    public static String SECRET_KEY;

    @Value("${cloud.aws.s3.endpoint}")
    private String endpoint;

    @Value("${cloud.aws.s3.region}")
    private String region;

    @Value("${cloud.aws.s3.bucket-name}")
    private String bucketName;

    @Value("${cloud.aws.s3.cdn-domain}")
    private String cdnDomain;

    @Value("${cloud.aws.s3.credentials.access-key}")
    private String accessKey;

    @Value("${cloud.aws.s3.credentials.secret-key}")
    private String secretKey;

    @Override
    public void afterPropertiesSet() throws Exception {
        END_POINT = this.endpoint;
        REGION = this.region;
        BUCKET_NAME = this.bucketName;
        CDN_DOMAIN = this.cdnDomain;
        ACCESS_KEY = this.accessKey;
        SECRET_KEY = this.secretKey;
    }
}

controller代码


    @RestController
    @RequestMapping("/api/file")
    @Api(tags = "文件上传接口")
    public class FileUploadController {
        
        @Resource
        private FileUploadService fileUploadService;
        
        @PostMapping("/r2/upload")
        @ApiOperation("Cloudflare R2文件上传")
        @ApiImplicitParam(name = "file", value = "上传文件", required = true, 
                           dataType = "MultipartFile")
        public CommonResult<String> uploadToR2(@RequestPart("file") MultipartFile file) {
            // 前置校验
            if (file.isEmpty()) {
                return CommonResult.error(ErrorCode.FILE_EMPTY);
            }
            // 调用服务层
            String url = fileUploadService.uploadToR2(file);
            return CommonResult.success(url, "上传成功");
        }
    }

serviceImpl核心代码


    @Service
    public class FileUploadServiceImpl implements FileUploadService {
        
        @Override
        public String uploadToR2(MultipartFile file) {
            try (InputStream inputStream = file.getInputStream()) {
                // 构建S3客户端
                S3Client s3Client = S3Client.builder()
                    .endpointOverride(URI.create(R2Config.END_POINT))
                    .credentialsProvider(() -> AwsBasicCredentials.create(
                        R2Config.ACCESS_KEY, R2Config.SECRET_KEY))
                    .region(Region.of(R2Config.REGION))
                    .build();
                
                // 生成唯一文件名(含日期路径)
                String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
                String originalFilename = file.getOriginalFilename();
                String fileExt = originalFilename.substring(originalFilename.lastIndexOf("."));
                String objectKey = datePath + "/" + UUID.randomUUID() + fileExt;
                
                // 构建上传请求
                PutObjectRequest putRequest = PutObjectRequest.builder()
                    .bucket(R2Config.BUCKET_NAME)
                    .key(objectKey)
                    .contentType(file.getContentType())
                    .build();
                
                // 执行上传
                s3Client.putObject(putRequest, 
                    RequestBody.fromInputStream(inputStream, file.getSize()));
                
                // 生成可访问URL(含CDN域名)
                return R2Config.CDN_DOMAIN + "/" + objectKey;
                
            } catch (IOException | S3Exception e) {
                log.error("R2文件上传失败: {}", e.getMessage(), e);
                throw new FileUploadException("文件上传失败,请稍后再试");
            }
        }
    }

接口测试

QQ20250523-165227.png

除此之外还可以限制文件大小,格式,设置定期删除等功能,示例中不再赘述。

五、参考资源

  1. 官方核心文档

  2. 社区最佳实践