最近开发需要用到对象存储,因此简单做个了解
什么是对象存储(Object-based Storage)
- 对象存储,主要操作对象是对象(Object)。
啊?这话听了好像跟没听一样,其实它的意思是指它关注的重点是文件对象本身。
那还是不懂啊,别急,可以看看别的存储:
- 块存储,关注的是文件的读写操作,操作的对象是磁盘(通俗讲,就是一块一块内存地去读写)
- 文件存储,关注的是文件目录,操作的对象是文件和文件夹(我们常见的文件目录树,就是我们平时硬盘的目录)
而我们现在说的对象存储,关注的是文件本身,因此,他就是只对文件负责,操作对象就是对象(文件本身),没有目录的概念。
我们可以用对象存储中的S3协议来看,它的操作命令只有:
- PUT
- GET
- DELETE
看吧,只有对文件的上传下载与删除,没有对磁盘写多少读多少的概念,也没有文件夹的概念,非常简洁,这就是对象存储。
对象存储数据组成
对象存储的存储基本单位是存储桶(bucket),我们的存储也是往桶里存数据,拿也是直接从桶里拿
一个对象(Object)里面有三个数据,分别为key、data、metadata:
- key
其实就是文件名,也是我们从桶里找到文件的唯一索引。 - data
这个就是文件数据本身,不用过多解释。
看完前面,不少学过数据结构的同学可能会想到,这不就是Map(Key,Value)吗?确实有点像,但是对象存储是把Metadata分开来存储的:
- Metadata
元数据,学过Linux的同学都知道,一个文件有自己的元信息,就是文件的大小、创建时间、修改时间、权限等,在传统的文件存储中,这些数据是被封装在文件(data)内部的,但是在对象存储中,这些东西是独立在Data之外的,好处也很明显,可以加快桶内文件的排序、查找。
我们怎么访问对象存储
对象存储提供了直接可读操作,如果我们有读的权限,那么直接通过运营商提供的链接即可访问内容
这里贴一个腾讯云COS对象存储的链接作为例子:
https://yl-blog-1304154650.cos.ap-guangzhou.myqcloud.com/kita.jpg
链接可以直接打开,发现是一张图片,那这个链接是怎么构成的呢?
yl-blog-1304154650
:这是桶的名称,代表要访问的桶cos.ap-guangzhou.myqcloud.com
:这是提供对象存储的服务商/kita.jpg
:代表要访问文件的key
这样子,就可以快速访问到文件了。
对象存储在实际开发的作用
目前,大多数项目都是前后端分离的形式,我们一开始都是想,前端访问到的文件图片等资源,都可以通过请求从后端中获得,上传的文件图片等资源,也可以直接存在后端中。
但是如果是这样的话,只有一个后端,当有大批量的数据存取时,后端的压力是不是有点太大了?
这个时候就可以用到对象存储了,我们转变思路,将本来存储到后端的文件本身转为存储到对象存储桶里,后端只需记录存储的url,在前端需要的时候,就可以直接返回url取得文件,这样子后端的压力是不是小很多了?
同时,我们还可以使用分布式存储,同时使用多个存储桶,让存储的压力更少,更灵活,优势还是非常明显的。
目前各大云服务厂商都有提供对象存储,动手能力强的同学也可以在自己的服务器上部署一个fastdfs来玩玩,至于教程,以后会写的......吧(咕!),Linux课上都有的!
腾讯云的对象存储COS在Java后端项目下的使用
写了那么多,就为了这个(捂脸)
参照的是:腾讯云官方使用文档
对此已经封装好了工具类,只需要在需要用的地方注入即可
@Component
public class CosClient {
@Value("${oss.sId}")
private String sId; // SecretId
@Value("${oss.sKey}")
private String sKey; // SecretKey
@Value("${oss.bucket}")
private String bucket; // 桶名
@Value("${oss.religion}")
private String region; // 服务区域
@Value("${oss.url}")
private String baseUrl; // 基础链接
private COSClient initCOSClient() {
TreeMap<String, Object> config = new TreeMap<String, Object>();
try {
//这里的 和 SecretKey 代表了用于申请临时密钥的永久身份(主账号、子账号等),子账号需要具有操作存储桶的权限。
String secretId = System.getenv(sId);//用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
String secretKey = System.getenv(sKey);//用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
// 替换为您的云 api 密钥 SecretId
config.put("secretId", sId);
// 替换为您的云 api 密钥 SecretKey
config.put("secretKey", sKey);
// 设置域名:
// 如果您使用了腾讯云 cvm,可以设置内部域名
//config.put("host", "sts.internal.tencentcloudapi.com");
// 临时密钥有效时长,单位是秒,默认 1800 秒,目前主账号最长 2 小时(即 7200 秒),子账号最长 36 小时(即 129600)秒
config.put("durationSeconds", 1800);
// 换成您的 bucket
config.put("bucket", bucket);
// 换成 bucket 所在地区
config.put("region", region);
// 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径
// 列举几种典型的前缀授权场景:
// 1、允许访问所有对象:"*"
// 2、允许访问指定的对象:"a/a1.txt", "b/b1.txt"
// 3、允许访问指定前缀的对象:"a*", "a/*", "b/*"
// 如果填写了“*”,将允许用户访问所有资源;除非业务需要,否则请按照最小权限原则授予用户相应的访问权限范围。
config.put("allowPrefixes", new String[]{"*"});
// 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
// 简单上传、表单上传和分块上传需要以下的权限,其他权限列表请参见 https://cloud.tencent.com/document/product/436/31923
String[] allowActions = new String[]{
// 简单上传
"name/cos:PutObject",
// 表单上传、小程序上传
"name/cos:PostObject",
// 分块上传
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload",
"name/cos:DeleteObject",
};
config.put("allowActions", allowActions);
Response response = CosStsClient.getCredential(config);
// 1 传入获取到的临时密钥 (tmpSecretId, tmpSecretKey, sessionToken)
String tmpSecretId = response.credentials.tmpSecretId;
String tmpSecretKey = response.credentials.tmpSecretKey;
String sessionToken = response.credentials.sessionToken;
BasicSessionCredentials cred = new BasicSessionCredentials(tmpSecretId, tmpSecretKey, sessionToken);
// 2 设置 bucket 的地域
// clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分
Region region = new Region(this.region); //COS_REGION 参数:配置成存储桶 bucket 的实际地域,例如 ap-beijing,更多 COS 地域的简称请参见 https://cloud.tencent.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(region);
// 3 生成 cos 客户端
return new COSClient(cred, clientConfig);
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("no valid secret !");
}
}
// 创建 TransferManager 实例,这个实例用来后续调用高级接口
private TransferManager createTransferManager(COSClient cosClient) {
// 自定义线程池大小,建议在客户端与 COS 网络充足(例如使用腾讯云的 CVM,同地域上传 COS)的情况下,设置成16或32即可,可较充分的利用网络资源
// 对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。
ExecutorService threadPool = Executors.newFixedThreadPool(32);
// 传入一个 threadpool, 若不传入线程池,默认 TransferManager 中会生成一个单线程的线程池。
TransferManager transferManager = new TransferManager(cosClient, threadPool);
// 设置高级接口的配置项
// 分块上传阈值和分块大小分别为 5MB 和 1MB
TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
transferManagerConfiguration.setMultipartUploadThreshold(5 * 1024 * 1024);
transferManagerConfiguration.setMinimumUploadPartSize(1 * 1024 * 1024);
transferManager.setConfiguration(transferManagerConfiguration);
return transferManager;
}
// 对请求的文件进行上传
// imgFile: 请求的文件
// filePath: 目标文件名(上传到桶的Key)
// 返回: 访问文件的URL
public String upLoadFile(MultipartFile imgFile, String filePath) {
COSClient cosClient = initCOSClient();
// 获得传输流实例
TransferManager transferManager = createTransferManager(cosClient);
// 存储桶的命名格式为 BucketName-APPID,此处填写的存储桶名称必须为此格式
String bucketName = bucket;
// 对象键(Key)是对象在存储桶中的唯一标识。
String key = filePath;
try {
ObjectMetadata objectMetadata = new ObjectMetadata();
// 上传的流如果能够获取准确的流长度,则推荐一定填写 content-length
// 如果确实没办法获取到,则下面这行可以省略,但同时高级接口也没办法使用分块上传了
objectMetadata.setContentLength(imgFile.getSize());
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, imgFile.getInputStream(), objectMetadata);
// 设置存储类型(如有需要,不需要请忽略此行代码), 默认是标准(Standard), 低频(standard_ia)
// 更多存储类型请参见 https://cloud.tencent.com/document/product/436/33417
putObjectRequest.setStorageClass(StorageClass.Standard_IA);
// 高级接口会返回一个异步结果Upload
// 可同步地调用 waitForUploadResult 方法等待上传完成,成功返回 UploadResult, 失败抛出异常
Upload upload = transferManager.upload(putObjectRequest);
UploadResult uploadResult = upload.waitForUploadResult();
return baseUrl + filePath;
} catch (CosServiceException e) {
e.printStackTrace();
} catch (CosClientException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
transferManager.shutdownNow();
cosClient.shutdown();
}
return "www";
}
// 从桶中删除文件,只需Key即可
public void deleteObject(String key) {
// 调用 COS 接口之前必须保证本进程存在一个 COSClient 实例,如果没有则创建
// 详细代码参见本页:简单操作 -> 创建 COSClient
COSClient cosClient = initCOSClient();
// 存储桶的命名格式为 BucketName-APPID,此处填写的存储桶名称必须为此格式
String bucketName = bucket;
// 对象键(Key)是对象在存储桶中的唯一标识。详情请参见 [对象键](https://cloud.tencent.com/document/product/436/13324)
try {
cosClient.deleteObject(bucketName, key);
} catch (CosServiceException e) {
e.printStackTrace();
} catch (CosClientException e) {
e.printStackTrace();
}
// 确认本进程不再使用 cosClient 实例之后,关闭即可
cosClient.shutdown();
}
}
其中,敏感常量信息使用的是SpringBoot提供的常量注入,只需在application.yml等文件写上相关信息即可完成注入,如下所示
oss:
sId: # secretId
sKey: # secretKey
bucket: # 桶的名称
religion: # 桶的服务区域
url: #访问的前置url,记得末尾要带“/”
在需要使用的类中使用注入即可,如下所示
@Resource
CosClient cosClient;
写在最后
本文仅为学习记录使用,如有错误,还请大家指出,共同进步!
其他的一些用法还可以继续通过腾讯云API文档查阅,继续完善工具类!