Spring Boot 集成阿里OSS对象存储
一、准备工作
-
基础知识
阿里
OSS
对象存储:云对象存储,一种分布式的文件存储实现方案
费用计算:阿里OSS
计费分三部分,存储费用、访问流量费用、请求费用 -
相关依赖
<!-- 阿里云oss --> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.17.0</version> </dependency>
最新依赖获取地址:
二、OSS
访问权限设置
-
1. 访问控制
OSS文件的访问控制一般都是基于用户的密钥和密匙来完成的;而授权的方式主要分为以下三种:
- 给用户直接赋予OSS读写权限(以下简称用户模式)
- 给用户分配用户组,给用户组赋予OSS权限(以下简称用户组模式)
- 给角色赋予OSS权限,修改RAM角色信任策略(以下简称ARN模式),给用户分配AliyunSTSAssumeRoleAccess权限,允许扮演RAM角色。
-
2. 创建用户密匙和密钥
在用户中选择访问OSS的用户,创建AccessKey(建议复制的同时将密匙文件下载下来),此时所生成的AccessKeyId和AccessKeySecret就是所需要使用的访问凭证。
-
3. 权限赋予
创建用户(略)
-
3.1 用户模式
选择用户,创建用户(如果使用已有用户,跳过此步),然后给用户设置OSS读写权限
-
3.2 用户组模式
切换到用户组,同理,创建用户组(如果使用已有用户组,跳过此步),给用户在赋予OSS读写权限
将指定的用户加入到当前组,该用户将会拥有组所拥有的所有权限
-
3.3 ARN模式
ARN模式比较复杂,主要基于角色来控制权限,用户和权限不再直接绑定。
a. 给用户赋予允许使用角色的权限:AliyunSTSAssumeRoleAccess
b. 在角色页按步骤创建角色
c. 为角色赋予OSS读写权限
d. 修改角色的策略
-
具体的策略模式描述:https://help.aliyun.com/document_detail/116819.html
-
下图中,ARN项目的内容需要复制下来,它时ARN模式下创建OssClient的必选项之一
-
Tips:当前信任策略指仅对用户akina-oss生效,其他人无法使用当前角色
三、OssClient的配置、创建
阿里OSS对文件的操作主要基于OssClient对象,而根据使用的授权模式不同,其OssClient创建对象的方式也不同。
1. 创建OssClient的方式
-
1.1 用户拥有OSS权限
-
用户模式和用户组模式下用户都属于直接拥有权限,通过Key就可进行OSS的相关操作。
// 直接放入端点、密钥和密匙 OSS ossClient = new OSSClientBuilder() .build("Endpoint", "AccessKeyId", "AccessKeySecret");
-
-
1.2 用户通过角色间接拥有OSS权限
// 先创建 STSAssumeRoleSessionCredentialsProvider 对象,放入地域、访问凭证、角色ARN码 // region:形如 cn-chengdu 的地域属性(官方地域属性) // accessKeyId:略,accessKeySecret:略 // roleArn;角色ARN代码,在创建角色修改信任策略时有提到 STSAssumeRoleSessionCredentialsProvider credentialsProvider = CredentialsProviderFactory .newSTSAssumeRoleSessionCredentialsProvider( region, accessKeyId, accessKeySecret, roleArn); OSS ossClient = new OSSClientBuilder().build("Endpoint", credentialsProvider);
2. 基于配置文件的实现
-
2.1. 配置和读取信息
-
编写配置文件:配置密钥、密匙和可能需要动态配置的信息(仅供参考)
## ali-oss-config.properties 配置文件,Spring读取配置时 aaa-bbb-ccc 会默认转换为驼峰格式 ## OSS 配置项 # (必选)endpoint 访问端点,一般根据oss桶的地域进行选择,此处以成都为例 oss.endpoint=oss-cn-chengdu.aliyuncs.com # (必选)授权密钥ID oss.access-key-id=LTAI5t************ # (必选)授权密匙 oss.access-key-secret=Mnwicrbz28UcG2L***************** # (必选)桶名称,创建桶时所取得名字 oss.bucket-name=oss-nep-akina # (自选)文件上传后存放的根目录文件夹 # 如果多个项目用了同一个桶,可以用项目名作为根目录文件夹,便于区分文件所属项目或者模块 oss.folder-root=blog
-
编写配置类:读取配置信息
// 交给ioc容器管理 @Component // 设置读取的配置项前缀,上述配置都是以oss开头 @ConfigurationProperties(value = "oss") // 设置读取的配置文件,文件应存放于resources文件夹下 @PropertySource("classpath:ali-oss-config.properties") public class AliOssProperties { /** 访问的域名 */ public String endpoint; /** 阿里云账号访问ID */ public String accessKeyId; /** 阿里云账号访问密匙 */ public String accessKeySecret; /** Bucket名称 */ public String bucketName; /** 主文件夹 */ public String folderRoot; /** get和set方法 */ }
-
3. 扩展:基于环境变量配置信息
-
在系统环境变量中配置指定项,在代码中取获取
// 从环境变量中获取键为OSS_ACCESS_KEY_ID的值 String accessKeyId = System.getenv("OSS_ACCESS_KEY_ID");
四、简单工具类模板
上传和删除功能,
实际使用中应对ossClient调用方法做try-catch-final处理,catch处理异常,final释放资源
/**
* AliOss工具类
* @author Ak
* @version 1.0
*/
public class AliOssManagerUtil {
private static AliOssProperties properties;
public AliOssManagerUtil(AliOssProperties properties) {
AliOssManagerUtil.properties = properties;
}
/**
* 上传文件到oss
* @param folder 文价夹
* @param file 文件
* @return 路径
*/
public static String uploadFile(String folder, MultipartFile file) throws Exception {
OSS ossClient = new OSSClientBuilder()
.build(properties.endpoint, properties.accessKeyId, properties.accessKeySecret);
// 处理文件名
String fileName = file.getOriginalFilename();
String newName = properties.folderRoot + "/" + folder + "/" + UUID.randomUUID();
if (null != fileName && !"".equals(fileName)) {
newName += fileName.substring(fileName.lastIndexOf('.'));
}
ossClient.putObject(properties.bucketName, newName, file.getInputStream());
ossClient.shutdown();
return newName;
}
/**
* 根据名字删除OSS服务器上的文件
* @param folder 文件所属文价夹 如"head"
* @param fileName 文件名 如:"cake.jpg"
*/
public static void deleteFile(String folder, String fileName){
OSS ossClient = new OSSClientBuilder()
.build(properties.endpoint, properties.accessKeyId, properties.accessKeySecret);
ossClient.deleteObject(properties.bucketName, properties.folderRoot + "/" + folder + "/" + fileName);
}
}
五、代码中几种常用操作
-
创建桶
Bucket bucket = ossClient.createBucket(bucketName);
-
文件上传
签名模式:后端提供签名,由前端直传到OSS(此处只列举后端)
// 获取签名,也可以创建一个对象来存方数据 public Map<String, String> getOssSign() { // 上传的地址,配置了CDN加速的也可以拼接成加速域名 String host = "https://" + properties.bucketName + "." + properties.endpoint; // 用户上传文件时文件所存放的文件夹 String dir = properties.folderRoot; // 创建 OSSClient 实例 OSS ossClient = null; try { // 策略过期时间(单位:秒) long expireTime = 100; // 计算出到期时间戳 long expireEndTime = System.currentTimeMillis() + expireTime * 1000; Date expiration = new Date(expireEndTime); // PostObject 请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为 5 * 1024 * 1024 * 1024 PolicyConditions policyConditions = new PolicyConditions(); policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000); policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); // 创建 OSSClient 实例 ossClient = new OSSClientBuilder() .build(properties.endpoint, properties.accessKeyId, properties.accessKeySecret); // 获取发布策略 String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions); byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8); // Base64加密 String encodedPolicy = BinaryUtil.toBase64String(binaryData); // 获取最终签名 String postSignature = ossClient.calculatePostSignature(postPolicy); // 返回签名以及 OSS 相关参数 Map<String, String> respMap = new LinkedHashMap<>(); respMap.put("accessid", properties.accessKeyId); respMap.put("policy", encodedPolicy); respMap.put("signature", postSignature); respMap.put("dir", dir); respMap.put("host", host); respMap.put("expire", String.valueOf(expireEndTime / 1000)); return respMap; } catch (Exception e) { e.printStackTrace(); } finally { if (ossClient != null) { ossClient.shutdown(); } } return null; }
后端模式
// 文本文件 PutObjectResult result = ossClient.putObject("oss-nep-akina", "testArn.text", new ByteArrayInputStream(content.getBytes())); // 任意文件,此处的file为MultipartFile类型,前端传的文件一般用该对象接收 PutObjectResult result = ossClient.putObject("oss-nep-akina", "test.text", file.getInputStream());
-
文件删除
// 删除文件 ossClient.deleteObject(properties.bucketName, fileFullPath);
-
文件列举
// 获取指定文件夹下的所有文件(不指定prefix会列举全部文件) ObjectListing objectListing = ossClient.listObjects(properties.bucketName, "blog/test/"); // objectListing.getObjectSummaries获取所有文件的描述信息 for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) { System.out.println(" - " + objectSummary.getKey() + " " + "(size = " + objectSummary.getSize() + ")"); }
参考文章:对象存储 OSS 阿里官方文档