前人栽树后人乘凉。就当做是前辈们的文章补充分支。
参考链接:
https://github.com/idinging/cloudflare-r2-demo
在springboot集成Cloudflare R2对象存储-优快云博客
玩转云服务(8):使用 Cloudflare R2 对象存储 - 知乎
情景:你的SpingBoot项目已经能够通过域名访问。你开通了CloudFlare的Free计划,并正确地将域名的DNS服务器托管到了CloudFlare上。
在账户主页找到R2。填写支付信息开通CloudFlare的R2服务。
一、在R2上创建一个存储桶,位置根据自己需要选择。
二、设置->自定义域->添加,添加自己的域名连接到你的存储桶。
域名以CloudFlare的DNS管理页面左上角为主。
创建完成后会自动给你添加一条R2类型的DNS记录,等待状态变为【活动】。
注意中文域名发生了转码,访问资源时是根据转码后的域名的URL访问的
三、SpringBoot项目添加Maven依赖
<!--与 Cloudflare R2 对象存储服务交互-->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.29.52</version>
</dependency>
<!--提供底层 HTTP 客户端功能,支持 AWS SDK 的 HTTP 请求发送和响应处理-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
四、获取你的配置信息
查看账户ID与访问端点endpoint
选择API->S3兼容API,点击创建API令牌。然后选择上面那一个。
创建成功,获得访问密钥ID和机密访问密钥。
注意复制下来,令牌值只显示一次,否则就只能刷新了。
访问密钥ID为 access-key,机密访问密钥为secret-key。
为S3客户端使用管辖权地特定的终结点为endpoint。
五、在SpringBoot配置文件中配置,并编写工具类获取配置信息
选择yml配置时:
# Cloudflare R2 配置
cloud:
aws:
s3:
endpoint: https://your-account-id.r2.cloudflarestorage.com #访问端点
bucket-name: your-bucket-name #存储桶名
cdn-domain: your-cdn-domain #存储桶连接的域名
region: auto #区域
credentials: #访问凭证
access-key: your-access-key #访问秘钥ID
secret-key: your-secret-key #机密访问秘钥
选择properties配置时:
# Cloudflare R2 配置
cloud.aws.s3.endpoint=https://your-account-id.r2.cloudflarestorage.com # 访问端点
cloud.aws.s3.bucket-name=your-bucket-name # 存储桶名
cloud.aws.s3.cdn-domain=your-cdn-domain # 存储桶连接的域名
cloud.aws.s3.region=auto # 区域
cloud.aws.s3.credentials.access-key=your-access-key # 访问秘钥ID
cloud.aws.s3.credentials.secret-key=your-secret-key # 机密访问秘钥
不是中文域名的忽略此条。
此处填写的cdn-domain没有中文域名和转码后的域名限制。
如果填写中文域名,访问资源时需要使用转码后的域名访问。
如果填写转码后的域名,上传后生成的资源URL可以直接访问。所以建议使用转码后的域名。
编写Util工具类获取配置中的参数:
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@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;
}
}
六、编写服务类,实现上传文件到CloudFlare R2的逻辑
import com.niit.util.R2FileUtils; //替换为实际的util类路径
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.regions.Region;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
@Service
public class CloudFlareR2ServiceImpl {
public String upload(MultipartFile file) throws IOException {
//使用try-with, S3Client在使用完毕后需要自动关闭
try(InputStream inputStream = file.getInputStream();
S3Client client = S3Client.builder()
.endpointOverride(URI.create(R2FileUtils.END_POINT)) //访问端点
.credentialsProvider(() -> AwsBasicCredentials.create(R2FileUtils.ACCESS_KEY, R2FileUtils.SECRET_KEY)) //访问凭证
.region(Region.of(R2FileUtils.REGION)) //地区
.build()) {
//获取当前日期, 利用当前日期构建对象键的路径, 如2025/05/27
//实际路径可根据开发情况修改
String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
//获取文件扩展名
//获取上传文件的原始文件名, 即包含扩展名
String originalFilename = file.getOriginalFilename();
assert originalFilename != null;
//获取源文件扩展名
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
//构建对象键, 包含日期路径、使用UUID随机生成的文件名
//实际文件名可根据开发情况修改
String objectKey = date + "/" + UUID.randomUUID() + fileExtension;
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(R2FileUtils.BUCKET_NAME) //设置存储桶名称
.key(objectKey) //设置对象键
.contentType(file.getContentType()) //设置内容类型
.build();
//使用S3Client将文件上传到R2
client.putObject(putObjectRequest, RequestBody.fromInputStream(inputStream, file.getSize()));
//返回上传后的文件访问URL
return R2FileUtils.CDN_DOMAIN + "/" + objectKey;
}catch (Exception e){
//抛异常
throw new IOException("文件上传失败"+e.getMessage());
}
}
}
七、调用与效果示例
在控制类中注入服务类,直接调用方法。
@Autowired
private CloudFlareR2ServiceImpl cloudFlareR2Service;
//在路径方法上使用, 上传到R2
String r2Url = cloudFlareR2Service.upload(photo);
//查看生成的路径, 可直接通过URL访问资源
System.out.println(r2Url);
以一个上传多用户信息的demo为例:
效果如图: