对象存储COS 通过签名上传文件实践
使用COS上传文件可以有多种方式
- 使用secretId、secretKey方式上传
- 使用临时密钥上传
- 使用签名上传
今天来分享下使用签名方式上传文件的实践,官方文档如下
腾讯云请求签名文档
腾讯云COS存储支持通过签名方式上传文件访问时只需要带有sdk生成的签名即可上传文件,我使用的是签名 URL的方式,使用的是java sdk
其实就是腾讯云COS sdk中提供了一个类COSClient,通过使用类中的generatePresignedUrl()方法,通过指定参数,会生成一个可以访问的url,url中有名为sign的param参数,如下(只是样例)
https://bucket-123456.cos.ap-beijing.myqcloud.com/test/test/test/test6.txt
?sign=q-sign-algorithm%3Dsha1%26q-ak%3DAKIDNFKHNM9udHmOvT%26q-sign-time%3D1717058465%3B1717061400%26q-key-time%3D1717058465%3B1717061400%26q-header-list%3Dcontent-md5%3Bcontent-type%26q-url-param-list%3D%26q-signature%3D91cc03c73bc6c64002f5
直接访问这个url就可以上传文件
- 在生成用于上传的签名链接时,还可以指定 Content-Type 或 Content-MD5 等头部,以便限制上传的媒体类型或限制上传的内容必须为指定内容
直接上代码
使用的依赖是
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.18</version>
</dependency>
主要用到如下的类
import com.qcloud.cos.COSClient;
该方法
public URL generatePresignedUrl(GeneratePresignedUrlRequest req) throws CosClientException{…}
首先你需要初始化COSClient
定义一个配置类保存COS相关配置
@Configuration
@Data
public class OssConfig {
@Value("${cos.secret-id}")
private String secretId;
@Value("${cos.secret-key}")
private String secretKey;
@Value("${cos.bucket-name}")
private String bucketName;
@Value("${cos.bucket-region}")
private String bucketRegion;
@Value("${cos.url-domain}")
private String urlDomain;
}
初始化COSClient
// 我这里使用@Configuration+@Bean注解在服务启动时初始化
@Configuration
public class DomainServiceImplConfig {
@Bean
public COSClient cosClient(OssConfig ossConfig) {
return generateCosClient(ossConfig.getSecretId(), ossConfig.getSecretKey(), ossConfig.getBucketRegion());
}
private COSClient generateCosClient(String secretId, String secretKey, String bucketRegion) {
//1.初始化用户身份信息
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
//2.设置bucket区域
Region region = new Region(bucketRegion);
ClientConfig clientConfig = new ClientConfig(region);
clientConfig.setConnectionTimeout(ObjectUploadConstants.COS_TIME_OUT);
clientConfig.setConnectionRequestTimeout(ObjectUploadConstants.COS_TIME_OUT);
//3.生成cos客户端
return new COSClient(cred, clientConfig);
}
}
定义一个请求体
@Data
public class PrivateBucketUploadSignBo {
// 用户自己指定文件要存储的cosKey位置
private String cosKey;
// 生成的签名过期时间
private Date expireTime;
// 使用签名上传文件时的请求方式 POST还是PUT
// 这里有个坑 腾讯云当前只支持通过PUT方式访问
private String method;
// 用户自己计算的Md5值,可以为空
private String contentMd5;
// 用户指定的MIME类型可以为空
private String contentType;
}
生成签名的逻辑如下
@Resource
private OssConfig ossConfig;
public String getBucketUploadSign(PrivateBucketUploadSignBo bo) {
String bucketName = ossConfig.getBucketName();
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, bo.getCosKey(), bo.getMethod());
request.setExpiration(bo.getExpireTime());
// 该字段为非必填,主要是用来限制文件上传类型,用户不填即不开启
if (StringUtils.isNotBlank(bo.getContentType())) {
request.setContentType(bo.getContentType());
}
// 该字段为非必填,用来校验文件完整性,用户不填即不开启
if (StringUtils.isNotBlank(bo.getContentMd5())) {
request.setContentMd5(bo.getContentMd5());
}
URL url = cosClient.generatePresignedUrl(request);
return url.toString();
}
补充说明
计算Md5的方法
该主要功能就是保证内容的完整,如果网络传输过程中有内容丢失,那么请求签名时的
最终计算得到的Md5形似 UV9RaUC3bem55119dY/PGA==
Java
public class MD5AndBase64Example {
public static void main(String[] args) {
String filePath = "D:\\20240530_092350_434_1_aplogcat.txt.zst"; // 请替换为你的文件路径
String content = null;
try {
List<String> lines = Files.readAllLines(Paths.get(filePath), StandardCharsets.UTF_8);
content = String.join(System.lineSeparator(), lines);
} catch (IOException e) {
e.printStackTrace();
}
String objectBody = content;
try {
// 计算MD5哈希值
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hashBytes = md.digest(objectBody.getBytes("UTF-8"));
// 将哈希值转换为十六进制字符串
StringBuilder sb = new StringBuilder();
for (byte b : hashBytes) {
sb.append(String.format("%02x", b & 0xff));
}
// 将哈希值的字节数组进行Base64编码
String base64Hash = Base64.getEncoder().encodeToString(hashBytes);
// 打印Base64编码的哈希值
System.out.println("Base64 Hash: " + base64Hash);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
// MD5算法通常总是可用的,但出于完整性考虑,我们仍然捕获异常
e.printStackTrace();
}
}
}
自测
首先明确如果在生成签名时,自己设置了
contentType 该参数如果设置了,之后上传时使用的Content-Type需要和签名时保持一致
contentMd5
该参数如果设置了,之后
- 获取签名时的contentMd5参数
- 上传时请求头的Content-MD5
- 上传时文件计算得到的实际MD5
三者需要保持一致
生成签名后会返回一个带sign参数的url链接
例如
https://bucket-123456.cos.ap-beijing.myqcloud.com/test/test/test/test6.txt
?sign=q-sign-algorithm%3Dsha1%26q-ak%3DAKIDNFKHNM9udHmOvT%26q-sign-time%3D1717058465%3B1717061400%26q-key-time%3D1717058465%3B1717061400%26q-header-list%3Dcontent-md5%3Bcontent-type%26q-url-param-list%3D%26q-signature%3D91cc03c73bc6c64002f5
直接使用在PostMan中发起PUT请求即可
如果生成签名时使用了这两个参数,那么请求头中这两个参数需要保持一致
问题来了文件在哪里传呢
在请求的Body中binary类型
直接选择文件即可
bingo!响应200,成功,再去COSBrowser中查查看,也确实有了