Aliyun OSS Sdk Quickstart

Aliyun OSS 是阿里云推出的云存储服务,本文将主要围绕如何制作一个 Aliyun-oss-sdk-spring-boot-starter 展开介绍。

功能分析

  • 文件上传
  • 获取不同实现类的文件上传配置, 提供前端服使用
  • 通用的参数配置, 支持应用选择自己的根目录

配置期望

1
2
3
4
5
6
7
8
9
10
11
12
# 文件默认的访问权限
file.default-access-control=public_read
# 文件默认的根文件夹, 所有上传的文件都会存放在此文件夹下
file.path=sky
# 文件存在放在 aliyun 的哪一个存储空间
file.aliyun.bucket=
# aliyun 对外服务的访问域名
file.aliyun.endpoint=
# aliyun 的访问密钥 ID
file.aliyun.access-key.id=
# aliyun 的访问密钥 SECRET
file.aliyun.access-key.secret=

实现步骤

  • 定义文件服务API
  • 依赖springboot-starter, 实现服务的自动注册
  • 依赖 aliyun-oss-sdk, 实现 Ali 文件服务

接下来, 我们看下具体的实现代码, 目前未上传到 GitHub 中, 我们可以看下大致思路.

具体实现

依赖管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<fastjson.version>1.2.60</fastjson.version>
<aliyun-sdk-oss.version>3.6.0</aliyun-sdk-oss.version>
</properties>

<dependencies>
<!--junit 依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

<!--aliyun oss 依赖-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun-sdk-oss.version}</version>
</dependency>

<!--json 依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>

<!--springboot starter 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--springboot starter 配置解析依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

配置管理

访问权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum AccessControlEnum {

/**
* 继承基类的权限, 依据实现者的默认实现
*/
DEFAULT,

/**
* 私有访问, 需生成URL才能访问
*/
PRIVATE,
/**
* 公有访问
*/
PUBLIC_READ,
/**
* 可读可写
*/
PUBLIC_READ_WRITE;

}

通用配置

以下代码请自行加入 Getter/Setter 方法, 或者使用 lombok

1
2
3
4
5
6
7
8
9
10
11
@Data
@ConfigurationProperties(prefix = "file")
public class FileProperties {

private AccessControlEnum defaultAccessControl = AccessControlEnum.DEFAULT;

private String path;

@NestedConfigurationProperty
private AliyunConfig aliyun;
}

阿里配置

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class AliyunConfig {

private String endpoint;
private String bucket;
private AccessKey accessKey;

public static class AccessKey {
private String id;
private String secret;
}
}

文件服务API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public interface FileService {

/**
* 上传文件
* @param filePath 文件路径
* @param file 需要上传的文件
* @param accessControl 访问权限
* @return
*/
String uploadFile(String filePath, File file, AccessControlEnum accessControl);

/**
* 上传文件
* @param filePath 文件路径
* @param inputStream 需要上传的流
* @param accessControl 访问权限
* @return
*/
String uploadFile(String filePath, InputStream inputStream, AccessControlEnum accessControl);

/**
* 获取上传文件的配置(供 web 端使用)
* @param expireSecond
* @return
* @throws UnsupportedEncodingException
*/
UploadConfigObj getUploadConfig(long expireSecond) throws UnsupportedEncodingException;

/**
* 服务销毁时, 调用
*/
void destroy();
}

上传服务配置

1
2
3
4
public interface UploadConfigObj {

String toJson();
}

Aliyun OSS 文件服务实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class AliyunFileService implements FileService {

private static final Logger logger = LoggerFactory.getLogger(AliyunFileService.class);

private AliyunConfig aliyunConfig;

private OSS ossClient;

private String bucket;

private String rootPath;

private CannedAccessControlList defaultAccessControl;

public AliyunFileService(FileProperties properties) {
rootPath = properties.getPath();
defaultAccessControl = toAliyunAccessControl(properties.getDefaultAccessControl());
aliyunConfig = properties.getAliyun();
bucket = aliyunConfig.getBucket();
ossClient = new OSSClientBuilder().build(aliyunConfig.getEndpoint(), aliyunConfig.getAccessKey().getId(),
aliyunConfig.getAccessKey().getSecret());
}

@Override
public String uploadFile(String fileName, InputStream inputStream, AccessControlEnum accessControl) {
String objectName = getFileObjectName(fileName);
ossClient.putObject(bucket, objectName, inputStream);
ossClient.setObjectAcl(bucket, objectName, toAliyunAccessControl(accessControl));
String fileUrl = getFileUrl(objectName);
logger.info("upload file to " + fileUrl);
return fileUrl;
}

@Override
public String uploadFile(String fileName, File file, AccessControlEnum accessControl) {
String objectName = getFileObjectName(fileName);
ossClient.putObject(bucket, objectName, file);
ossClient.setObjectAcl(bucket, objectName, toAliyunAccessControl(accessControl));
String fileUrl = getFileUrl(objectName);
logger.info("upload file to " + fileUrl);
return fileUrl;
}

@Override
public AliConfigUploadObj getUploadConfig(long expireSecond) throws UnsupportedEncodingException {
String dir = getFileObjectName("");
long expireEndTime = System.currentTimeMillis() + expireSecond * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConditions = new PolicyConditions();
policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions);
byte[] binaryData = postPolicy.getBytes(CommonConstant.CHARSET_UTF8);
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);

AliConfigUploadObj uploadObj =new AliConfigUploadObj();
uploadObj.setAccessId(aliyunConfig.getAccessKey().getId());
uploadObj.setSignature(postSignature);
uploadObj.setExpire(String.valueOf(expireEndTime / 1000));
uploadObj.setHost(aliyunConfig.getHost());
uploadObj.setPolicy(encodedPolicy);

return uploadObj;
}

@Override
public void destroy() {
ossClient.shutdown();
logger.info("destroy Aliyun OSS Client");
}

private String getFileUrl(String objectName) {
return String.join("", aliyunConfig.getHost(), "/", objectName);
}

private String getFileObjectName(String objectName) {
return String.join("", rootPath, "/", objectName);
}

private CannedAccessControlList toAliyunAccessControl(AccessControlEnum accessControl) {
if (accessControl == null) {
return defaultAccessControl;
}
CannedAccessControlList cannedAccessControlList = null;
switch (accessControl) {
case DEFAULT:
cannedAccessControlList = CannedAccessControlList.Default;
case PRIVATE:
cannedAccessControlList = CannedAccessControlList.Private;
break;
case PUBLIC_READ:
cannedAccessControlList = CannedAccessControlList.PublicRead;
break;
case PUBLIC_READ_WRITE:
cannedAccessControlList = CannedAccessControlList.PublicReadWrite;
break;
default:
cannedAccessControlList = defaultAccessControl;
break;
}
return cannedAccessControlList;
}

}

ali 上传配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class AliConfigUploadObj implements UploadConfigObj {

private String accessId;

private String policy;

private String signature;

private String host;

private String expire;

private String dir;

@Override
public String toJson() {
return JSONObject.toJSONString(this);
}
}

Spring自动配置

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@EnableConfigurationProperties(FileProperties.class)
public class FileAutoConfiguration {

@Autowired
private FileProperties properties;

@Bean(name = "fileService")
public FileService fileService() {
return new AliyunFileService(properties);
}
}

META-INF/spring.factories

1
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zenlayer.oss.file.FileAutoConfiguration