文章目录
- 参考
- 准备
- 创建bucket
- endpoint 和 bucket域名的访问路径
- AccessKey和OSS的开发文档
- Springboot整合OSS
- 引入依赖
- AliyunOssConfig
- AliyunOssProperties
- applicatioin.yml
- 简单上传和下载
- 使用签名URL进行临时授权访问
- 生成以PUT方法访问的签名URL来上传文件
- 通过签名URL临时授权简单上传文件
- 使用签名URL临时授权下载文件
- 服务端签名直传
参考
阿里云java的sdk官方示例代码
前端不暴露ak/sk直接上传阿里云oss的方案
前端直传阿里云OSS
阿里云 OSS 客户端直传 Policy 模式使用
服务端签名直传并设置上传回调官方文档Java
使用阿里云STS临时token完成阿里云OSS图片上传(Springboot+Vue)
使用STS临时访问凭证通过客户端直连OSS对象存储服务器
使用STS临时访问凭证访问OSS-官方文档
阿里云OSS STS最佳实践,看这一篇就够了
前端通过STS方式直传至阿里云OSS(包含文件上传、下载和自动刷新stsToken)
准备
创建bucket
endpoint 和 bucket域名的访问路径
endpoint 和 bucket域名的访问路径
AccessKey和OSS的开发文档
创建AccessKey(图中点击进去即可创建)和查看OSS的开发文档
开发文档
AccessKey
Springboot整合OSS
引入依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
</dependency>
AliyunOssConfig
@EnableConfigurationProperties(AliyunOssProperties.class)
@Component
public class AliyunOssConfig {
@Autowired
private AliyunOssProperties aliyunOssProperties;
@Bean
public OSS ossClient() {
OSS oss = new OSSClientBuilder().build(
aliyunOssProperties.getEndpoint(),
aliyunOssProperties.getAccessKeyId(),
aliyunOssProperties.getSecretAccessKey()
);
return oss;
}
}
AliyunOssProperties
@Data
@ConfigurationProperties(prefix = "oss")
public class AliyunOssProperties {
private String endpoint;
private String accessKeyId;
private String secretAccessKey;
// 上传到哪个桶
private String bucketName;
// 上传的桶中的文件的访问域名
private String bucketDomain;
}
applicatioin.yml
oss:
accessKeyId: ~~~
secretAccessKey: ~~~
endpoint: https://oss-cn-shenzhen.aliyuncs.com
bucketName: zzhua-oss-bucket2
bucketDomain: https://${oss.bucketName}.oss-cn-shenzhen.aliyuncs.com
简单上传和下载
@SpringBootTest(classes = MinioApp.class)
public class TestOssApp {
@Autowired
private OSSClient ossClient;
@Autowired
private AliyunOssProperties aliyunOssProperties;
// 流式上传
@Test
void testStreamUpload() throws Exception {
String filePath= "C:\\Users\\zzhua195\\Desktop\\soft-dev.png";
File file = new File(filePath);
InputStream inputStream = new FileInputStream(file);
String objectName = file.getName();
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(
aliyunOssProperties.getBucketName(),
objectName,
inputStream
);
// 创建PutObject请求。
PutObjectResult result = ossClient.putObject(putObjectRequest);
System.out.println(file.getName());
// 注意需要关闭 阻止公共访问, 读写权限改为公共读
// 如: https://zzhua-oss-bucket2.oss-cn-shenzhen.aliyuncs.com/soft-dev.png
System.out.println("访问路径为: " + aliyunOssProperties.getBucketDomain() + "/" + objectName);
}
// 流式下载
@Test
void testStreamDownload() throws Exception {
String filePath= "C:\\Users\\zzhua195\\Desktop\\soft-dev1111.png";
File file = new File(filePath);
try {
OSSObject ossObject = null;
ossObject = ossClient.getObject(aliyunOssProperties.getBucketName(), "soft-dev.png");
FileOutputStream fos = new FileOutputStream(file);
StreamUtils.copy(ossObject.getObjectContent(), fos);
fos.close();
} catch (Exception e) {
System.out.println("获取资源失败");
}
}
}
使用签名URL进行临时授权访问
bucket开启阻止公共访问, 读写权限设置为私有, 此时直接使用{bucketDomain}/{objectName}无法直接访问资源。此时,需要生成1个url, 并且携带签名信息才能访问
@Test
void testGeneratedSignedUrl() {
String objectName = "20240825/soft-dev.png";
GeneratePresignedUrlRequest generatePresignedUrlReq = new GeneratePresignedUrlRequest(
aliyunOssProperties.getBucketName(),
objectName,
HttpMethod.GET
);
// 必须设置有效时间
generatePresignedUrlReq.setExpiration(new Date(new Date().getTime() + 3 * 60 * 1000L));
URL url = ossClient.generatePresignedUrl(generatePresignedUrlReq);
// 示例: https://zzhua-oss-bucket.oss-cn-shenzhen.aliyuncs.com/20240825/soft-dev.png?Expires=1724635526&OSSAccessKeyId=LTAI5tDVake7ZcZrwbCDjP97&Signature=3BT%2BCJPRx3YDxOxRS8qtVhTHJHI%3D
System.out.println(url.toString());
}
生成以PUT方法访问的签名URL来上传文件
@Test
void testGenrateSignedPut() throws Exception {
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
aliyunOssProperties.getBucketName(),
"naughty.gif",
HttpMethod.PUT
);
// 设置签名URL过期时间,单位为毫秒。本示例以设置过期时间为1小时为例。
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
request.setExpiration(expiration);
// 设置ContentType。
request.setContentType("image/gif");
// 设置自定义元数据。
request.addUserMetadata("author", "zzhua");
// 生成签名URL。
URL signedUrl = ossClient.generatePresignedUrl(request);
System.out.println(signedUrl);
Map<String, String> requestHeaders = new HashMap<String, String>();
// 设置ContentType,必须和生成签名URL时设置的ContentType一致。
requestHeaders.put(HttpHeaders.CONTENT_TYPE, "image/gif");
// 设置自定义元数据。
requestHeaders.put(OSS_USER_METADATA_PREFIX + "author", "zzhua");
FileInputStream inputStream = new FileInputStream("C:\\Users\\zzhua195\\Desktop\\naughty.gif");
// 使用签名URL上传文件。
PutObjectResult putObjectResult = ossClient.putObject(signedUrl, inputStream, -1, requestHeaders, true);
}
通过签名URL临时授权简单上传文件
使用下面代码可以先生成1个上传的签名url,然后使用这个url来put上传文件(但是在postman中就是上传失败,报SignatureDoesNotMatch错误)
@Test
void testGenratePutSimpleUpload() throws Exception {
// 设置请求头。
Map<String, String> headers = new HashMap<String, String>();
/*// 指定Object的存储类型。
headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
// 指定ContentType。
headers.put(OSSHeaders.CONTENT_TYPE, "text/txt");*/
// 设置用户自定义元数据。
Map<String, String> userMetadata = new HashMap<String, String>();
/*userMetadata.put("key1","value1");
userMetadata.put("key2","value2");*/
URL signedUrl = null;
try {
// 指定生成的签名URL过期时间,单位为毫秒。本示例以设置过期时间为1小时为例。
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
// 生成签名URL。
String objectName = "naughty.gif";
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
aliyunOssProperties.getBucketName(),
objectName,
HttpMethod.PUT
);
// 设置过期时间。
request.setExpiration(expiration);
// 将请求头加入到request中。
request.setHeaders(headers);
// 添加用户自定义元数据。
request.setUserMetadata(userMetadata);
// 通过HTTP PUT请求生成签名URL。
signedUrl = ossClient.generatePresignedUrl(request);
// 打印签名URL。
System.out.println("signed url for putObject: " + signedUrl);
} catch (OSSException oe) {
System.out.println("生成失败~~~");
}
if (1 > 0) {
return;
}
// 使用生成的签名url来上传文件
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
try {
HttpPut put = new HttpPut(signedUrl.toString());
HttpEntity entity = new FileEntity(new File("C:\\Users\\zzhua195\\Desktop\\naughty.gif"));
put.setEntity(entity);
// 如果生成签名URL时设置了header参数,例如用户元数据,存储类型等,则调用签名URL上传文件时,也需要将这些参数发送至服务端。如果签名和发送至服务端的不一致,会报签名错误。
for(Map.Entry header: headers.entrySet()){
put.addHeader(header.getKey().toString(),header.getValue().toString());
}
for(Map.Entry meta: userMetadata.entrySet()){
// 如果使用userMeta,sdk内部会为userMeta拼接"x-oss-meta-"前缀。当您使用其他方式生成签名URL进行上传时,userMeta也需要拼接"x-oss-meta-"前缀。
put.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
}
httpClient = HttpClients.createDefault();
response = httpClient.execute(put);
System.out.println("返回上传状态码:"+response.getStatusLine().getStatusCode());
if(response.getStatusLine().getStatusCode() == 200){
System.out.println("使用网络库上传成功");
}
System.out.println(response.toString());
} catch (Exception e){
e.printStackTrace();
} finally {
response.close();
httpClient.close();
}
}
使用签名URL临时授权下载文件
跟使用签名URL进行临时授权访问是一样的
@Test
void testGenerateDownload() throws IOException {
// 设置请求头。
Map<String, String> headers = new HashMap<String, String>();
/*// 指定Object的存储类型。
headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
// 指定ContentType。
headers.put(OSSHeaders.CONTENT_TYPE, "text/txt");*/
// 设置用户自定义元数据。
Map<String, String> userMetadata = new HashMap<String, String>();
/*userMetadata.put("key1","value1");
userMetadata.put("key2","value2");*/
URL signedUrl = null;
try {
// 指定生成的签名URL过期时间,单位为毫秒。本示例以设置过期时间为1小时为例。
Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
// 生成签名URL。
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
aliyunOssProperties.getBucketName(),
"20240825/soft-dev.png",
HttpMethod.GET
);
// 设置过期时间。
request.setExpiration(expiration);
// 将请求头加入到request中。
request.setHeaders(headers);
// 添加用户自定义元数据。
request.setUserMetadata(userMetadata);
// 设置查询参数。
// Map<String, String> queryParam = new HashMap<String, String>();
// 指定IP地址或者IP地址段,对应日志中sourceIpFromSource的值。
// queryParam.put("x-oss-ac-source-ip","192.0.2.0");
// 将子网掩码转换为二进制,然后填写转换结果中1的数量。
// queryParam.put("x-oss-ac-subnet-mask","32");
// 指定VPC ID。
// queryParam.put("x-oss-ac-vpc-id","vpc-12345678");
// 指定是否允许转发请求。
// queryParam.put("x-oss-ac-forward-allow","true");
// request.setQueryParameter(queryParam);
// 设置单链接限速,单位为bit,例如限速100 KB/s。
// request.setTrafficLimit(100 * 1024 * 8);
// 通过HTTP GET请求生成签名URL。
signedUrl = ossClient.generatePresignedUrl(request);
// 打印签名URL。
System.out.println("signed url for putObject: " + signedUrl);
} catch (Exception e) {
System.out.println("发生错误" + e);
}
// 通过签名URL下载文件,以HttpClients为例说明。
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
try {
HttpGet get = new HttpGet(signedUrl.toString());
// 如果生成签名URL时设置了header参数,例如用户元数据,存储类型等,则调用签名URL下载文件时,也需要将这些参数发送至服务端。如果签名和发送至服务端的不一致,会报签名错误。
for(Map.Entry header: headers.entrySet()){
get.addHeader(header.getKey().toString(),header.getValue().toString());
}
for(Map.Entry meta: userMetadata.entrySet()){
// 如果使用userMeta,sdk内部会为userMeta拼接"x-oss-meta-"前缀。当您使用其他方式生成签名URL进行下载时,userMeta也需要拼接"x-oss-meta-"前缀。
get.addHeader("x-oss-meta-"+meta.getKey().toString(), meta.getValue().toString());
}
httpClient = HttpClients.createDefault();
response = httpClient.execute(get);
System.out.println("返回下载状态码:"+response.getStatusLine().getStatusCode());
if(response.getStatusLine().getStatusCode() == 200){
System.out.println("使用网络库下载成功");
}
System.out.println(response.toString());
// 保存文件到磁盘。
DataInputStream in = null;
OutputStream out = null;
try {
in = new DataInputStream(response.getEntity().getContent());
String pathName = "C:\\Users\\zzhua195\\Desktop\\ttt.png";
out = new DataOutputStream(new FileOutputStream(pathName));
int bytes = 0;
byte[] bufferOut = new byte[1024];
while ((bytes = in.read(bufferOut)) != -1) {
out.write(bufferOut, 0, bytes);
}
} catch (Exception e){
e.printStackTrace();
} finally {
in.close();
out.close();
}
} catch (Exception e){
e.printStackTrace();
} finally {
response.close();
httpClient.close();
}
}
服务端签名直传
@GetMapping("/getPolicy")
public JSONObject getPolicy() throws Exception {
// host的格式为 bucketname.endpoint
// String host = StringFormatter.concat("https://", bucketName, ".", endpoint).getValue();
String host = aliyunOssProperties.getBucketDomain();
// callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
// String callbackUrl = "http://88.88.88.88:8888";
// 每一天产生一个文件夹
String dir = LocalDate.now().toString() + "/"; // 用户上传文件时指定的前缀,如果是 / 则自动检测为文件夹。
JSONObject jsonObject = new JSONObject();
long expireTime = 600;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000; //过期时间 100 秒
Date expiration = new Date(expireEndTime);
// PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
jsonObject.put("OSSAccessKeyId", aliyunOssProperties.getAccessKeyId());
jsonObject.put("policy", encodedPolicy);
jsonObject.put("signature", postSignature);
jsonObject.put("dir", dir);
jsonObject.put("host", host);
jsonObject.put("expire", String.valueOf(expireEndTime / 1000));
return jsonObject;
}