1 pom依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- https://cloud.tencent.com/document/product/436/10199-->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.89</version>
</dependency>
2 yml配置
cos:
client:
accessKey: aaa
secretKey: bbb
region: ccc
bucket: dddd
3 Cos配置文件
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.region.Region;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 腾讯云对象存储客户端
*/
@Configuration
@ConfigurationProperties(prefix = "cos.client")
@Data
public class CosClientConfig {
/**
* accessKey
*/
private String accessKey;
/**
* secretKey
*/
private String secretKey;
/**
* 区域
*/
private String region;
/**
* 桶名
*/
private String bucket;
@Bean
public COSClient cosClient() {
// 初始化用户身份信息(secretId, secretKey)
COSCredentials cred = new BasicCOSCredentials(accessKey, secretKey);
// 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region(region));
// 生成cos客户端
return new COSClient(cred, clientConfig);
}
}
4 核心操作类
import cn.huawei.config.CosClientConfig;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.exception.MultiObjectDeleteException;
import com.qcloud.cos.model.*;
import com.qcloud.cos.transfer.Download;
import com.qcloud.cos.transfer.TransferManager;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Cos 对象存储操作
*/
@Component
public class CosManager {
@Resource
private CosClientConfig cosClientConfig;
@Resource
private COSClient cosClient;
// 复用下载对象
private TransferManager transferManager;
// bean 加载完成后执行
@PostConstruct
public void init() {
System.out.println("Bean 初始化成功");
// 多线程并发上传下载
// 自定义线程池大小,建议在客户端与 COS 网络充足(例如使用腾讯云的 CVM,同地域上传 COS)的情况下,设置成16或32即可,可较充分的利用网络资源
// 对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。
ExecutorService threadPool = Executors.newFixedThreadPool(32);
transferManager = new TransferManager(cosClient, threadPool);
}
/**
* 上传对象
*
* @param key 唯一键
* @param localFilePath 本地文件路径
* @return
*/
public PutObjectResult putObject(String key, String localFilePath) {
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
new File(localFilePath));
return cosClient.putObject(putObjectRequest);
}
/**
* 上传对象
*
* @param key 唯一键
* @param file 文件
* @return
*/
public PutObjectResult putObject(String key, File file) {
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
file);
return cosClient.putObject(putObjectRequest);
}
/**
* 下载对象
*
* @param key 唯一键
* @return
*/
public COSObject getObject(String key) {
GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), key);
return cosClient.getObject(getObjectRequest);
}
/**
* 下载对象到本地文件
*
* @param key
* @param localFilePath
* @return
*/
public Download download(String key, String localFilePath) throws InterruptedException {
File downloadFile = new File(localFilePath);
GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), key);
Download download = transferManager.download(getObjectRequest, downloadFile);
download.waitForCompletion();
return download;
}
/**
* 删除对象
*
* @param key
* @throws CosClientException
* @throws CosServiceException
*/
public void deleteObject(String key) throws CosClientException, CosServiceException {
cosClient.deleteObject(cosClientConfig.getBucket(), key);
}
/**
* 批量删除对象
*
* @param keyList
* @return
* @throws MultiObjectDeleteException
* @throws CosClientException
* @throws CosServiceException
*/
public DeleteObjectsResult deleteObjects(List<String> keyList)
throws MultiObjectDeleteException, CosClientException, CosServiceException {
if (keyList.isEmpty()) {
return null;
}
DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(cosClientConfig.getBucket());
// 设置要删除的key列表, 最多一次删除1000个
ArrayList<DeleteObjectsRequest.KeyVersion> keyVersions = new ArrayList<>();
// 传入要删除的文件名
// 注意文件名不允许以正斜线 / 或者反斜线 \ 开头,例如:
// 存储桶目录下有a/b/c.txt文件,如果要删除,只能是 keyList.add(new KeyVersion("a/b/c.txt")), 若使用 keyList.add(new KeyVersion("/a/b/c.txt"))会导致删除不成功
for (String key : keyList) {
keyVersions.add(new DeleteObjectsRequest.KeyVersion(key));
}
deleteObjectsRequest.setKeys(keyVersions);
DeleteObjectsResult deleteObjectsResult = cosClient.deleteObjects(deleteObjectsRequest);
return deleteObjectsResult;
}
/**
* 删除目录
*
* @param delPrefix
* @throws CosClientException
* @throws CosServiceException
*/
public void deleteDir(String delPrefix) throws CosClientException, CosServiceException {
ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
// 设置 bucket 名称
listObjectsRequest.setBucketName(cosClientConfig.getBucket());
// prefix 表示列出的对象名以 prefix 为前缀
// 这里填要列出的目录的相对 bucket 的路径
listObjectsRequest.setPrefix(delPrefix);
// 设置最大遍历出多少个对象, 一次 listobject 最大支持1000
listObjectsRequest.setMaxKeys(1000);
// 保存每次列出的结果
ObjectListing objectListing = null;
do {
objectListing = cosClient.listObjects(listObjectsRequest);
// 这里保存列出的对象列表
List<COSObjectSummary> cosObjectSummaries = objectListing.getObjectSummaries();
if (cosObjectSummaries.isEmpty()) {
break;
}
ArrayList<DeleteObjectsRequest.KeyVersion> delObjects = new ArrayList<DeleteObjectsRequest.KeyVersion>();
for (COSObjectSummary cosObjectSummary : cosObjectSummaries) {
delObjects.add(new DeleteObjectsRequest.KeyVersion(cosObjectSummary.getKey()));
}
DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(cosClientConfig.getBucket());
deleteObjectsRequest.setKeys(delObjects);
cosClient.deleteObjects(deleteObjectsRequest);
// 标记下一次开始的位置
String nextMarker = objectListing.getNextMarker();
listObjectsRequest.setMarker(nextMarker);
} while (objectListing.isTruncated());
}
}
5 controller测试
import cn.huawei.domain.BaseResponse;
import cn.huawei.domain.ResultUtils;
import cn.huawei.manager.CosManager;
import com.qcloud.cos.model.COSObject;
import com.qcloud.cos.model.COSObjectInputStream;
import com.qcloud.cos.utils.IOUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
/**
* 文件接口
*/
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
@Resource
private CosManager cosManager;
/**
* 测试文件下载
*
* @param filepath
* @param response
* @return
*/
@GetMapping("/test/download/")
public void testDownloadFile(String filepath, HttpServletResponse response) throws IOException {
COSObjectInputStream cosObjectInput = null;
try {
COSObject cosObject = cosManager.getObject(filepath);
cosObjectInput = cosObject.getObjectContent();
// 处理下载到的流
byte[] bytes = IOUtils.toByteArray(cosObjectInput);
// 设置响应头
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + filepath);
// 写入响应
response.getOutputStream().write(bytes);
response.getOutputStream().flush();
} catch (Exception e) {
log.error("file download error, filepath = " + filepath, e);
throw new RuntimeException( "下载失败");
} finally {
if (cosObjectInput != null) {
cosObjectInput.close();
}
}
}
/**
* 文件上传
*
* @param multipartFile
* @param request
* @return
*/
@PostMapping("/upload")
public BaseResponse<String> uploadFile(@RequestPart("file") MultipartFile multipartFile,
HttpServletRequest request) {
validFile(multipartFile);
// 文件目录:根据业务、用户来划分
String uuid = RandomStringUtils.randomAlphanumeric(8);
String filename = uuid + "-" + multipartFile.getOriginalFilename();
String filepath = String.format("/%s/%s", "user_avatar", filename);
File file = null;
try {
// 上传文件
file = File.createTempFile(filepath, null);
multipartFile.transferTo(file);
cosManager.putObject(filepath, file);
// 返回可访问地址
return ResultUtils.success(filepath);
} catch (Exception e) {
log.error("file upload error, filepath = " + filepath, e);
throw new RuntimeException( "上传失败");
} finally {
if (file != null) {
// 删除临时文件
boolean delete = file.delete();
if (!delete) {
log.error("file delete error, filepath = {}", filepath);
}
}
}
}
/**
* 校验文件
*
* @param multipartFile
*/
private void validFile(MultipartFile multipartFile) {
// 文件大小
long fileSize = multipartFile.getSize();
// 文件后缀
int i = multipartFile.getOriginalFilename().lastIndexOf(".");
String fileSuffix = multipartFile.getOriginalFilename().substring(i+1);
final long ONE_M = 1024 * 1024L;
if (fileSize > ONE_M) {
throw new RuntimeException("文件大小不能超过 1M");
}
if (!Arrays.asList("jpeg", "jpg", "svg", "png", "webp").contains(fileSuffix)) {
throw new RuntimeException("文件类型错误");
}
}
}
7 其它辅助文件
import java.io.Serializable;
import lombok.Data;
/**
* 通用返回类
*/
@Data
public class BaseResponse<T> implements Serializable {
private int code;
private T data;
private String message;
public BaseResponse(int code, T data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
public BaseResponse(int code, T data) {
this(code, data, "");
}
}
------------------------------------------------------
package cn.huawei.domain;
/**
* 返回工具类
*/
public class ResultUtils {
/**
* 成功
*
* @param data
* @param <T>
* @return
*/
public static <T> BaseResponse<T> success(T data) {
return new BaseResponse<>(0, data, "ok");
}
/**
* 失败
*
* @param code
* @param message
* @return
*/
public static BaseResponse error(int code, String message) {
return new BaseResponse(code, null, message);
}
}