package cn.lili.utils;
|
|
import cn.lili.cos.COSConfigProperty;
|
import cn.lili.cos.CosSTS;
|
import cn.lili.modules.lmk.domain.entity.LmkFile;
|
import com.qcloud.cos.COSClient;
|
import com.qcloud.cos.ClientConfig;
|
import com.qcloud.cos.auth.BasicSessionCredentials;
|
import com.qcloud.cos.exception.CosClientException;
|
import com.qcloud.cos.exception.CosServiceException;
|
import com.qcloud.cos.http.HttpMethodName;
|
import com.qcloud.cos.model.*;
|
import com.qcloud.cos.region.Region;
|
import com.tencent.cloud.CosStsClient;
|
import com.tencent.cloud.Policy;
|
import com.tencent.cloud.Response;
|
import com.tencent.cloud.Statement;
|
import com.tencent.cloud.cos.util.Jackson;
|
import lombok.RequiredArgsConstructor;
|
import org.apache.commons.collections4.CollectionUtils;
|
import org.springframework.stereotype.Component;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import javax.servlet.http.HttpServletResponse;
|
import java.io.*;
|
import java.net.URL;
|
import java.net.URLEncoder;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
/**
|
* @author:xp
|
* @date:2025/5/16 16:31
|
*/
|
@Component
|
@RequiredArgsConstructor
|
public class COSUtil {
|
|
private final COSConfigProperty cosConfigProperty;
|
|
/**
|
* 获取sts临时访问凭证
|
*
|
* @return
|
*/
|
public CosSTS getSTS() {
|
TreeMap<String, Object> config = new TreeMap<String, Object>();
|
try {
|
config.put("secretId", cosConfigProperty.getSecretId());
|
config.put("secretKey", cosConfigProperty.getSecretKey());
|
|
// 初始化 policy
|
Policy policy = new Policy();
|
|
// 设置域名:
|
// 如果您使用了腾讯云 cvm,可以设置内部域名
|
//config.put("host", "sts.internal.tencentcloudapi.com");
|
|
// 临时密钥有效时长,单位是秒,默认 1800 秒,目前主账号最长 2 小时(即 7200 秒),子账号最长 36 小时(即 129600)秒
|
config.put("durationSeconds", cosConfigProperty.getDurationSeconds());
|
// 换成您的 bucket
|
config.put("bucket", cosConfigProperty.getBucket());
|
// 换成 bucket 所在地区
|
config.put("region", cosConfigProperty.getRegion());
|
|
// 开始构建一条 statement
|
Statement statement = new Statement();
|
// 声明设置的结果是允许操作
|
statement.setEffect("allow");
|
/**
|
* 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
|
* 权限列表请参见 https://cloud.tencent.com/document/product/436/31923
|
* 规则为 {project}:{interfaceName}
|
* project : 产品缩写 cos相关授权为值为cos,数据万象(数据处理)相关授权值为ci
|
* 授权所有接口用*表示,例如 cos:*,ci:*
|
* 添加一批操作权限 :
|
*/
|
statement.addActions(cosConfigProperty.getActions());
|
|
/**
|
* 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径
|
* 资源表达式规则分对象存储(cos)和数据万象(ci)两种
|
* 数据处理、审核相关接口需要授予ci资源权限
|
* cos : qcs::cos:{region}:uid/{appid}:{bucket}/{path}
|
* ci : qcs::ci:{region}:uid/{appid}:bucket/{bucket}/{path}
|
* 列举几种典型的{path}授权场景:
|
* 1、允许访问所有对象:"*"
|
* 2、允许访问指定的对象:"a/a1.txt", "b/b1.txt"
|
* 3、允许访问指定前缀的对象:"a*", "a/*", "b/*"
|
* 如果填写了“*”,将允许用户访问所有资源;除非业务需要,否则请按照最小权限原则授予用户相应的访问权限范围。
|
*
|
* 示例:授权examplebucket-1250000000 bucket目录下的所有资源给cos和ci 授权两条Resource
|
*/
|
statement.addResources(cosConfigProperty.getResources());
|
|
// 把一条 statement 添加到 policy
|
// 可以添加多条
|
policy.addStatement(statement);
|
// 将 Policy 示例转化成 String,可以使用任何 json 转化方式,这里是本 SDK 自带的推荐方式
|
config.put("policy", Jackson.toJsonPrettyString(policy));
|
|
Response response = CosStsClient.getCredential(config);
|
System.out.println(response.credentials.tmpSecretId);
|
System.out.println(response.credentials.tmpSecretKey);
|
System.out.println(response.credentials.sessionToken);
|
|
CosSTS cosSTS = new CosSTS();
|
cosSTS.setTmpSecretId(response.credentials.tmpSecretId);
|
cosSTS.setTmpSecretKey(response.credentials.tmpSecretKey);
|
cosSTS.setSessionToken(response.credentials.sessionToken);
|
Date now = new Date();
|
cosSTS.setStsStartTime(now.getTime() / 1000);
|
// 预留30s的请求时间,防止给小程序的结束时间超过实际的结束时间
|
cosSTS.setStsEndTime(cosSTS.getStsStartTime() + cosConfigProperty.getDurationSeconds() - 30);
|
cosSTS.setBucket(cosConfigProperty.getBucket());
|
cosSTS.setRegion(cosConfigProperty.getRegion());
|
cosSTS.setEndpoint(cosConfigProperty.getEndpoint());
|
return cosSTS;
|
} catch (Exception e) {
|
e.printStackTrace();
|
throw new IllegalArgumentException("get sts error");
|
}
|
}
|
|
/**
|
* 初始化cos客户端
|
*
|
* @return COSClient cos客户端
|
*/
|
public COSClient initClient() {
|
CosSTS sts = this.getSTS();
|
BasicSessionCredentials cred = new BasicSessionCredentials(sts.getTmpSecretId(), sts.getTmpSecretKey(), sts.getSessionToken());
|
// 2 设置 bucket 的地域
|
// clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分
|
Region region = new Region(cosConfigProperty.getRegion()); //COS_REGION 参数:配置成存储桶 bucket 的实际地域,例如 ap-beijing,更多 COS 地域的简称请参见 https://cloud.tencent.com/document/product/436/6224
|
ClientConfig clientConfig = new ClientConfig(region);
|
// 3 生成 cos 客户端
|
COSClient cosClient = new COSClient(cred, clientConfig);
|
return cosClient;
|
}
|
|
/**
|
* 最简单的上传文件
|
*
|
* @param fileInput 文件
|
* @param fileInfo 文件信息
|
* @return fileKey 文件路径,或者叫唯一标识
|
*/
|
public void upload(InputStream fileInput, LmkFile fileInfo) {
|
COSClient cosClient = this.initClient();
|
ObjectMetadata objectMetadata = new ObjectMetadata();
|
objectMetadata.setContentType(fileInfo.getFileType());
|
objectMetadata.setContentLength(fileInfo.getFileSize());
|
|
// objectMetadata.setContentDisposition("attachment;filename=" + URLEncoder.encode(fileInfo.getOriginalFilename(), "UTF-8"));
|
// 指定文件上传到 COS 上的路径,即对象键。例如对象键为 folder/picture.jpg,则表示将文件 picture.jpg 上传到 folder 路径下
|
PutObjectRequest putObjectRequest = new PutObjectRequest(cosConfigProperty.getBucket(), fileInfo.getFileKey(), fileInput, objectMetadata);
|
PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
|
System.out.println(putObjectResult);
|
cosClient.shutdown();
|
}
|
|
/**
|
* 下载文件
|
*
|
* @param fileKey 文件路径,或者叫唯一标识
|
* @param response
|
*/
|
public void download(String fileKey, HttpServletResponse response) {
|
COSClient cosClient = this.initClient();
|
GetObjectRequest getObjectRequest = new GetObjectRequest(cosConfigProperty.getBucket(), fileKey);
|
|
try {
|
// 获取COS对象
|
COSObject cosObject = cosClient.getObject(getObjectRequest);
|
|
// 获取对象元数据
|
ObjectMetadata metadata = cosObject.getObjectMetadata();
|
String contentType = metadata.getContentType();
|
long contentLength = metadata.getContentLength();
|
String filename = fileKey.substring(fileKey.lastIndexOf('/') + 1);
|
|
// 设置响应头
|
response.setContentType(contentType != null ? contentType : "application/octet-stream");
|
response.setContentLengthLong(contentLength);
|
response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(filename, "UTF-8") + "\"");
|
|
// 获取输入流和输出流
|
try (InputStream cosObjectInput = cosObject.getObjectContent();
|
OutputStream responseOutputStream = response.getOutputStream()) {
|
|
// 使用缓冲区传输数据
|
byte[] buffer = new byte[4096];
|
int bytesRead;
|
while ((bytesRead = cosObjectInput.read(buffer)) != -1) {
|
responseOutputStream.write(buffer, 0, bytesRead);
|
}
|
responseOutputStream.flush();
|
}
|
} catch (CosServiceException e) {
|
// COS服务异常
|
e.printStackTrace();
|
throw new RuntimeException("存储服务异常");
|
} catch (CosClientException e) {
|
// COS客户端异常
|
e.printStackTrace();
|
throw new RuntimeException("存储服务客户端异常");
|
} catch (IOException e) {
|
// IO异常
|
e.printStackTrace();
|
throw new RuntimeException("文件读取异常");
|
} finally {
|
// 确保COS客户端关闭
|
if (cosClient != null) {
|
cosClient.shutdown();
|
}
|
}
|
}
|
|
|
/**
|
* 获取在线访问文件地址
|
*
|
* @param fileKey
|
* @return
|
*/
|
public String getPreviewUrl(String fileKey) {
|
return cosConfigProperty.getEndpoint() + "/" + fileKey;
|
}
|
|
|
/**
|
* 删除单个文件
|
*
|
* @param fileKey
|
*/
|
public void deleteFile(String fileKey) {
|
COSClient cosClient = this.initClient();
|
try {
|
cosClient.deleteObject(cosConfigProperty.getBucket(), fileKey);
|
} catch (Exception e) {
|
e.printStackTrace();
|
throw new RuntimeException("文件删除失败");
|
} finally {
|
cosClient.shutdown();
|
}
|
}
|
|
/**
|
* 删除多个文件
|
*
|
* @param fileKeys
|
*/
|
public void deleteFiles(List<String> fileKeys) {
|
if (CollectionUtils.isEmpty(fileKeys)) {
|
return;
|
}
|
List<DeleteObjectsRequest.KeyVersion> keys = fileKeys.stream().map(key -> new DeleteObjectsRequest.KeyVersion(key)).collect(Collectors.toList());
|
COSClient cosClient = this.initClient();
|
try {
|
DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(cosConfigProperty.getBucket());
|
deleteObjectsRequest.setKeys(keys);
|
cosClient.deleteObjects(deleteObjectsRequest);
|
} catch (Exception e) {
|
e.printStackTrace();
|
throw new RuntimeException("文件删除失败");
|
} finally {
|
cosClient.shutdown();
|
}
|
}
|
|
}
|