一、前言
最近恰好因为公司需要,涉及到了文件的上传下载这一块,于是想到了使用腾讯云的COS
(云对象存储)来完成这部分的功能实现。
(一)、为什么要使用云对象存储服务?
理由很简单,项目涉及到用户的文件传输时,一旦用户规模较大,很容易出现服务器难以承载的情况,这种时候如果有一个专门的服务商提供文件上传和下载的服务,就能大大减小压力(不管是QPS
还是硬盘空间)。
(二)、云对象存储的设计思路
以我的项目为例,我的思路如下:前端将文件(以特定的key
值)上传至云对象服务器,同时将文件信息和key
值传递给后端。后端将其持久化到关系型数据库中。当我们需要访问某个对象附带的文件时,只需前端从后端获取对象json
信息,通过key
,从云对象服务器获取文件即可。
二、操作步骤
(一)、在腾讯云 对象存储控制台 开通腾讯云对象存储(COS)服务
这部分没啥好说,看准根据需求,买就完事了。
(二)、在腾讯云 对象存储控制台 创建一个 Bucket
Bucket
是腾讯云对象存储的基本单位,可以类比于MYSQL
中的数据库。创建的过程也可以说是按部就班。值得一提的是读写权限这一块。需要根据需求谨慎选择,建议只读不加密,写加密。
(三)、在访问管理控制台中的 API 密钥管理 页面里获取 APPID,并创建 SecretId、SecretKey。
这里卡了我一会,主要是没找到他说的菜单位置,管理控制台需要按照以下这个路径去找。
“首页——控制台——平台工具——访问管理”
(四)、编写一个请求签名算法程序(或使用任何一种服务端 SDK)
这部分应该没有人会自己写吧,有写好的不用,重复造轮子麻烦又没必要。
1.导入签名SDK
直接根据自己的语言,找到对应的SDK,开用。
我用的是JavaScript的SDK,因为是前端直接对接云存储服务。
由于前端是使用了工程化的vue项目,因此直接使用如下命令进行SDK的下载:
npm i cos-js-sdk-v5 --save
2.封装操作方法
这块根据官方参考代码做基础,自己适当封装就好。
我为了演示方便,直接将永久密钥放在前端,不想管安全风险啥的就学我(笑)。
下面就封装了三个基础的方法,分别用于上传、下载和删除文件。
import COS from 'cos-js-sdk-v5';
enum env {
COS_SecretId = 'xxx',
COS_SecretKey = 'xxx',
COS_Region = 'ap-guangzhou',
COS_Bucket = 'xxx',
}
const cos = new COS({
SecretId: env.COS_SecretId
, // 推荐使用环境变量获取;用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
SecretKey: env.COS_SecretKey,// 推荐使用环境变量获取;用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://cloud.tencent.com/document/product/598/37140
});
export const uploadFile = (file:File) => {
cos.uploadFile({
Bucket: env.COS_Bucket, /* 填写自己的 bucket,必须字段 */
Region: env.COS_Region, /* 存储桶所在地域,必须字段 */
Key: '1.jpg', /* 存储在桶里的对象键(例如:1.jpg,a/b/test.txt,图片.jpg)支持中文,必须字段 */
Body: file, // 上传文件对象
SliceSize: 1024 * 1024 * 5, /* 触发分块上传的阈值,超过5MB使用分块上传,小于5MB使用简单上传。可自行设置,非必须 */
onProgress: function(progressData) {
console.log(JSON.stringify(progressData));
}
}, function(err, data) {
if (err) {
console.log('上传失败', err);
} else {
console.log('上传成功');
}
});
}
export const downloadFile = (key:string) => {
cos.getObjectUrl({
Bucket: env.COS_Bucket, /* 填写自己的 bucket,必须字段 */
Region: env.COS_Region, /* 存储桶所在地域,必须字段 */
Key: key, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
}, function(err, data) {
if (err) return console.log(err);
/* 通过指定 response-content-disposition=attachment 实现强制下载 */
const downloadUrl = data.Url + (data.Url.indexOf('?') > -1 ? '&' : '?') + 'response-content-disposition=attachment';
/* 可拼接 filename 来实现下载时重命名 */
/* downloadUrl += ';filename=myname'; */
// (推荐使用 window.open()方式)这里是新窗口打开 url,如果需要在当前窗口打开,可以使用隐藏的 iframe 下载,或使用 a 标签 download 属性协助下载
window.open(downloadUrl);
});
}
export const deleteFile = (key:string) => {
cos.deleteObject({
Bucket: env.COS_Bucket, /* 填写自己的 bucket,必须字段 */
Region: env.COS_Region, /* 存储桶所在地域,必须字段 */
Key: key, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
}, function(err, data) {
console.log(err || data);
});
}
(五)、完善上传和下载的业务逻辑
1.上传文件
要上传一个文件,不可能只有一个方法,首先得让用户选择文件。
对此,我采取的策略是:先让用户触发input的change事件,然后再通过HTMLInputElement
上的属性来获取file对象,最终和封装好的上传方法衔接。
由于label
标签能触发内部input
元素的上传事件,因此能轻易的通过点击label来完成上传效果(而input完全不用展示)。
<label class="tool-button">
<input type="file" @change="uploadHandler" v-show="false" />
<div class="img-wrapper">
<img :src="toutiaoIcon" />
</div>
<h4>上传</h4>
</label>
接下来就是通过event事件对象获取file,然后将其作为参数传递给封装好的方法。
const uploadHandler = (event:Event) => {
const target = event?.target as HTMLInputElement
if(target.files && target.files.length > 0)
uploadFile(target?.files[0])
}
2.下载和删除文件
这块其实没啥好说的了,js
的方法封装好,无非是加个按钮触发一下,传入指定的key即可。