先说场景:
用户通过微信打开页面,在上面可以上传图片、视频,也可以查看自己上传的图片和视频。
场景很简单,但是基本可以涉及到腾讯生态的常见开发技术。
一、需求调研
1.1、整理场景规则:
1、只能通过微信打开;
2、多用户使用,需要做用户隔离,每个用户的图片视频存放在自己标识的路径下;
3、不想购买服务器,也就是尽量使用Serverless;
1.2、梳理整体流程:
1、用户打开网页,浏览CDN托管的网页地址。
2、返回的网页中,需要访问云函数,进行安全信息的初始化和访问。
3、云函数访问微信接口,完成初始化,并返回安全信息。
4、用户获取到安全信息后,可以访问COS文件对象,访问时带上以上步骤获取的安全信息。

1.3、确定技术方案
场景基于微信环境,所以整体都考虑使用腾讯系产品:
页面 - 使用腾讯云的CDN托管静态网站
云函数 - 使用腾讯云的函数服务SCF
存储 - 使用腾讯云的对象存储COS
1.4、拆解开发步骤:
1、开发H5页面
2、部署存储方案
3、开发云函数,对接H5页面
4、完善H5页面,使用腾讯授权信息,开发上传和查询功能
二、开发H5页面
H5页面是用户的整个操作界面,包含上传和查看的功能。
可以直接用AI生成,现在各种AI平台,使用自己顺手的就行,比如直接用浏览器访问的kimi/deepseek/chatgpt:
帮我生成一个在微信端使用的H5页面。
这里我使用了开发工具 Trae IDE (国际版: Trae AI),大家可以选用自己顺手的AI辅助工具,如果有合适的也请推荐给我。
AI在这里针对微信环境做了相关判断,类似代码:
html
<div id="wxOnly" class="wx-warning hidden">
⚠️ 请使用微信打开本页面
</div>
javascript
// 1. 检测微信环境
if (!/MicroMessenger/i.test(navigator.userAgent)) {
document.getElementById('wxOnly').classList.remove('hidden');
}
可以继续询问AI,完善整个页面:
这是一个在微信里面上传图片和视频的页面,请针对微信端进行页面优化。

一句话搞定一个页面(包括提示词中还有两个错别字,AI都完美理解)。直接本地运行查看效果,有一些bug或者调整都可以反复询问AI得到解决。
接下来我们要把这个页面部署到公网:使用腾讯云CDN来托管此页面。
三、部署存储方案
这里选择了腾讯云的CDN分发(用来托管H5页面)和COS存储桶(用来存储网站、上传的图片和视频)。
3.1、账号配置
1、注册腾讯云账号。
2、完成实名认证。
3.2、COS对象存储
2、创建存储桶,创建完后对应的存储桶列表: COS存储桶列表。



通过上面步骤,创建两个存储桶 site存储桶(桶名:site-123456,用来存储静态网站,并托管给CDN) 和 upload存储桶(桶名:upload-123456,用来存储网页上传的图片和视频):

此时已经可以上传文件并进行访问。为了更好的满足场景,接下来进行存储桶的配置。
3.3、site存储桶 配置
site存储桶是用来托管网页的,所以需要开启静态网站。
1、开启静态网站
这一步让存储桶除了提供存储能力,还可以提供网站服务能力。



其中的访问节点即为公网访问地址,可以直接访问,只是这个地址又长又不好看,接下来我们优化一下。
2、自定义CDN加速域名
从上面可以看到,访问地址又长又长,这一步就是让这个访问即友好,又快捷。
选择 域名与传输管理 - 自定义CDN加速域名 - 添加域名:

这里有两种类型域名:
- 自定义CDN加速域名,用自己的域名来访问存储的文件,而且还带了CDN的缓存和加速。
- 自定义源站域名,用自己的域名来访问存储的文件,仅此而已。
所以我们这里选择的是CDN加速域名。
配置CDN域名信息:

这里的源站类型包含:默认源站/静态网站源站/全球加速源站。这里选择静态网站源站。
3、配置HTTPS证书
现在都在推HTTPS证书,能有的咱都要有,配置CDN的HTTPS证书需要转到CDN控制台: CDN证书配置。
选择 证书管理 - 证书配置 - 更新:

填写证书信息:

这里填入证书内容,点击提交,然后在证书列表中看到证书状态为 "配置成功" 即可。

我们也可以在SSL证书管理界面(地址:https://console.cloud.tencent.com/ssl)查看所有的SSL证书:

到这里,我们有了一个配置完全的静态网站,带HTTPS,带加速分发,不再需要自己买服务器。接下来配置上传文件的存储桶。
3.4、upload存储桶 配置
1、自定义源站域名
选择 域名与传输管理 - 自定义源站域名 - 添加域名:

配置域名信息和类型:

这里的源站类型选择 默认源站,跟 site 存储桶的自定义域名有一点区别,点击创建就添加了自定义域名。接下来给自定义域名配置HTTPS证书。
2、配置HTTPS证书
选择 域名与传输管理 - 自定义源站域名 - 绑定证书:

这里上传证书信息:

到这里我们就配置好了 upload 桶的域名和HTTPS证书。
四、开发云函数,对接H5页面
在前面的步骤,已经部署好存储方案,接下来就可以用云函数来对接使用存储服务。
云函数的使用,主要是基于Serverless的需求,可以直接使用云函数来替换后台应用,而不需要自己搭建服务器来构建后台应用。
基于我们的技术方案,云函数需要对接腾讯来获取授权信息,同时对接前端来提供服务:
1、获取当前微信用户的openid,用来定义每个用户的存储路径;
2、获取对象存储COS的临时密钥,用来后续访问COS;
3、提供与前端页面对接的能力 - 函数URL;
4.1、云函数开发流程
这里分为三步来执行:
1、本地用相同的环境开发好代码;
2、然后再上传部署到云函数;
3、在线调整代码,并部署和测试;
4.2、本地开发代码
1、新建项目
代码结构如下:

node_modules:依赖库,将上传到云端
index.js: 入口脚本,将上传到云端
package.json: 项目配置,将上传到云端
package-lock.json: 项目锁定配置,将上传到云端
test.js: 本地测试文件,不需要上传
2、test.js
javascript
// test.js
const { main_handler } = require('./index.js'); // 引入云函数入口
(async () => {
const mockEvent = { body: JSON.stringify({ code: '123' }) }; // 模拟 API 网关事件
const mockContext = { requestId: 'local-test' };
const res = await main_handler(mockEvent, mockContext);
console.log('返回结果:', res);
})();
这里模拟了云函数的运行环境,函数入参为 code,同时传递了事件信息和context信息。
3、index.js
javascript
// 通过code获取微信用户的openid
async function getOpenId(code) {
return {
statusCode: 200,
data: '123'
};
}
// 获取cos的临时密钥
async function getCosAuth(openId) {
return {
statusCode: 200,
data: 'abc'
};
}
exports.main_handler = async (event) => {
// 入参 code 必填
const body = JSON.parse(event.body || '{}');
const { code } = body;
if (!code) {
return {
statusCode: 400,
headers:{'Content-Type':'application/json'},
body: JSON.stringify({
error: '缺少code'
})
};
}
const openId = await getOpenId(code);
const cosAuth = await getCosAuth(openId);
return {
statusCode: 200,
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({cosAuth:cosAuth, openid:openId})
};
};
其中的main_handler为入口函数,同时获取cos临时密钥和openid,这里先定义过程和结构,后续可以用实际获取代码来替换。
4、package.json
javascript
{
"name": "scfDemoCode",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"qcloud-cos-sts":"^3.1.3"
}
}
这里的 qcloud-cos-sts 是COS封装的SDK库。
本地运行包安装,后续可以上传到云函数。
javascript
npm install
这里注意本地nodejs的版本,后续云函数使用18.15,本地也最好使用同样的版本来构建。
命令运行后,本地将生成 node_modules,此时本地环境已准备好,接下来准备搭建云函数。
4.3、部署到SCF云函数
接下来把上述代码部署到云函数。
1、开通云函数
地址:https://console.cloud.tencent.com/scf/index 。
2、新建函数
地址:https://console.cloud.tencent.com/scf/list 。
选择 函数服务 - 新建:

这里配置函数:

- 从头开始 - 这里选择从头开始,纯手搓;
- 函数类型 - 默认是事件函数,对于目前的场景,也可以直接选择Web函数,都可以满足;
- 函数名称 - 友好名字,这里我们起名 getCosToken,自己好理解的就可以;
- 运行环境 - 使用自己熟悉的语言即可,这里选择 nodejs 18.15;
- 提交方法 - 这里初次使用可以选择"本地上传zip包",按照 5.3 步骤,本地开发好后直接上传压缩包,当然也可以选择"在线编辑";
勾选协议后,点击"完成",即创建了新的云函数。
从函数列表查看:

此时的云函数已经准备就绪,那么如何触发呢?也就是如何给我们的前端页面来进行调用呢?这就需要用到"函数URL"(对于Web函数类型的云函数,类似,可以尝试下,后续也补充一下)。
3、开通公网访问
在上面的函数列表点击函数名,进入函数管理页面:

点击"函数URL",点击"新建函数URL":

然后设置函数配置:

这里的大部分配置都和HTTP的请求方式是一样的,所以可以按照自己需求来定制配置,上述配置是可以满足任意请求,所以,因为为了测试。
配置完成的样子:

此时可以通过这里的公网访问地址来访问云函数,可以用postman等工具来测试下,因为这里支持所有请求方式,包括get请求,所以可以直接用浏览器测试下。
4、自定义域名
我们上面开通的函数URL是一串很长的地址,这里给他设置一个友好域名地址。
自定义域名地址:https://console.cloud.tencent.com/scf/domain。

选择 自定义域名 - 添加自定义域名,

这里主要注意下"路径映射"。路径是域名访问的地址,函数是对应的云函数,可以同事映射配置多个云函数。
如上图,表示访问域名任意一个地址都可以访问云函数 getCosToken。一般建议每个云函数对应一个具体的地址,这样为了演示使用了 /* 表示任意路径访问,都会访问到 getCosToken 云函数。
到这里,我们的云函数环境已准备好。
五、完善云函数
在实际开发中,每一个环节不是独立串行的,往往是迭代且交错进行。
在完成上述开发云函数后,我们就可以在前端页面调用云函数来确保流程的通顺,只是云函数的逻辑是mock的,这里开始实际获取openid和cos的临时密钥。
5.1、获取微信用户的openid
每个用户上传的文件都保存在自己的路径下,需要使用用户的标识来区分。最容易想到的是openid,也考虑过手机号,但是:

好了,还是使用openid。
5.1.1、获取openid的代码
javascript
async function getOpenId(code) {
const wxAppId = 'wx12345'
const wxSecret = 'abcdefg'
const url = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${wxAppId}&secret=${wxSecret}&code=${code}&grant_type=authorization_code`;
const wxRes = await request(url);
if (wxRes.errcode) {
return {
statusCode: 400,
data: wxRes.errmsg + ', code:' + code,
};
}
return {
statusCode: 200,
data: wxRes.openid
};
}
这里的wxAppId和wxSecret是微信开发信息,获取的方式也有两种选择,一种是使用免费的测试号,一种是使用认证的服务号。
5.1.2、方案1 - 测试号
如果临时使用,可以使用测试号,完全免费。
测试号地址:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index。

这里两部分信息:
测试号信息 - 就是我们需要的 wxAppId和wxSecret。
JS接口安全域名 - 前面4.3.2配置的CDN静态网站的域名(site.xxxxx.com)。这样可以通过网站来访问。
5.1.3、方案2 - 认证的服务号
如果有米,或者公司使用,那就需要使用认证的服务号。获取开发所需要的信息也有两个地方。
1、从服务号获取
登录服务号:https://mp.weixin.qq.com/。

选择 设置与开发 - 开发接口管理 - 基础配置,可以看到开发信息。
JS接口安全域名和网页授权域名设置:

选择 设置与开发 - 账号设置 - 功能设置,可以查看和设置域名。


2、从微信开发者平台获取
地址:https://developers.weixin.qq.com/console/index。

选择 我的业务 - 服务号,
在右侧选择对应的服务号,然后在"基础信息"中可以查看对应的信息。
5.2、获取对象存储COS的临时密钥
由于访问对象存储需要密钥,考虑到安全,用正式密钥生成临时密钥,发布到前端页面,然后在前端用临时密钥来访问COS。临时密钥有时效限制,能够一定程度保障安全。
使用临时密钥访问COS:
https://cloud.tencent.com/document/product/436/68283
获取临时密钥的接口文档:
https://cloud.tencent.com/document/product/436/14048
获取临时密钥的调试工具:
5.2.1、获取COS临时密钥的代码
javascript
async function getCosAuth(openId) {
const bucketName = 'upload-1234567';
var appId = bucketName.substr(1 + bucketName.lastIndexOf('-'));
var policy = {
'version': '2.0',
'statement': [{
'action': [
// 简单上传
'name/cos:PutObject',
'name/cos:PostObject',
// 获取文件
'name/cos:GetObject',
'name/cos:GetBucket',
// 分片上传
'name/cos:InitiateMultipartUpload',
'name/cos:ListMultipartUploads',
'name/cos:ListParts',
'name/cos:UploadPart',
'name/cos:CompleteMultipartUpload',
// 简单上传和分片,需要以上权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
],
'effect': 'allow',
'principal': { 'qcs': ['*'] },
'resource': [
'qcs::cos:' + config.region + ':uid/' + appId + ':' + config.bucket + '/' + openId + '/*',
],
}],
};
return await STS.getCredential({
secretId: config.secretId,
secretKey: config.secretKey,
proxy: config.proxy,
durationSeconds: config.durationSeconds,
region: config.region,
endpoint: config.endpoint,
policy: policy,
});
}
这里的bucketName对应的是upload存储桶的名称 - "upload-123456"。
到这里,我们的云函数功能基本完成。
六、前端页面
到这里,我们整理下(实在是这篇文太长了):
- 我们有了一个网站,https://site.xxxxx.com;
- 我们有了存储桶,可以存放上传的图片和视频;
- 我们已经准备好了云函数,它负责跟各种服务沟通,返回我们所需要的授权和信息;
接下来重构前端页面,来实际调用COS的相关功能。
6.1、COS入门指南
SDK概览:
https://cloud.tencent.com/document/product/436/6474
SDK下载地址:
https://github.com/tencentyun/cos-js-sdk-v5/blob/master/dist/cos-js-sdk-v5.min.js
快速入门:
https://cloud.tencent.com/document/product/436/11459
自主诊断工具:
https://console.cloud.tencent.com/cos/diagnose
6.2、前端页面逻辑
1、初始化COS
有两种 获取临时密钥进行初始化的方式,如下:
javascript
import COS from 'cos-js-sdk-v5';
const cos = new COS({
getAuthorization: async function (options, callback) {
const data = await fetchSts('xxxx'); // 需自行实现获取临时密钥逻辑
callback({
TmpSecretId: data.credentials.tmpSecretId,
TmpSecretKey: data.credentials.tmpSecretKey,
SecurityToken: data.credentials.sessionToken,
// 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
StartTime: data.startTime, // 时间戳,单位秒,如:1580000000
ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000
ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
});
}
});
javascript
import COS from 'cos-js-sdk-v5';
const cos = new COS({
// 其他配置项可参考下方 初始化配置项
// getAuthorization 必选参数
getAuthorization: function (options, callback) {
// 初始化时不会调用,只有调用 cos 方法(例如 cos.putObject)时才会进入
// 异步获取临时密钥
// 服务端 JS 示例:https://github.com/tencentyun/cos-js-sdk-v5/blob/master/server/
// 服务端其他语言参考 COS STS SDK :https://github.com/tencentyun/qcloud-cos-sts-sdk
// STS 详细文档指引看:https://cloud.tencent.com/document/product/436/14048
const stsUrl = 'http://example.com/server/sts'; // stsUrl 替换成您自己的后端服务
fetch(stsUrl)
.then(response => response.json())
.then(data => {
if (!data || !data.credentials) {
return console.error('credentials invalid:\n' + JSON.stringify(data, null, 2))
}
// 检查credentials格式
const { credentials } = data;
console.log(credentials);
callback({
TmpSecretId: credentials.tmpSecretId,
TmpSecretKey: credentials.tmpSecretKey,
SecurityToken: credentials.sessionToken,
// 建议返回服务器时间作为签名的开始时间,避免客户端本地时间偏差过大导致签名错误
StartTime: data.startTime, // 时间戳,单位秒,如:1580000000
ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000
ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用
});
})
.catch(error => {
console.error('获取临时密钥失败', error);
});
}
});
export default cos;
2、上传
sdk: https://cloud.tencent.com/document/product/436/64960
api: https://cloud.tencent.com/document/product/436/7749
javascript
cos.putObject({
Bucket: bucket,
Region: region,
Key,
Body: file,
Headers: {
'x-cos-acl': 'public-read'
},
onProgress: function (info) {
// ...
}
}, function (err, data) {
if (err) {
// 失败
} else {
// 成功
}
});
3、查看
sdk: https://cloud.tencent.com/document/product/436/64962
api: https://cloud.tencent.com/document/product/436/7734
javascript
cos.getBucket({
Bucket: bucket,
Region: region,
Prefix: `${openid}`
}, function (err, data) {
if (err) {
console.log('获取文件列表失败');
return;
}
// 过滤掉 Size 为 0 的对象
const contents = (data.Contents || []).filter(item => Number(item.Size) > 0);
if (contents.length === 0) {
console.log('暂无上传文件');
return;
}
// 按时间倒序排列
allFiles = contents.sort((a, b) =>
new Date(b.LastModified) - new Date(a.LastModified)
);
// 显示
display(allFiles);
});
}
七、优化
7.1、上传视频优化
7.1.1、需求分析
上传的某些视频,在页面不能预览,求助一下AI:

这里我们就需要使用腾讯云的"任务与工作流",进行转码后存储。
7.1.2、视频转码的任务与工作流
进入COS存储桶 upload-123456:

选择 任务与工作流 - 工作流管理 - 创建工作流,

点击工作流节点的 + 号,选择 媒体处理 - 音视频转码,

这里按照需要配置:
- 目标存储桶 - 选择 upload-123456;
- 目标文件名 - 转码后生成的文件名,这里是 {inputName}_{RunId}.{ext},原始文件名为 demo.mp4,会生成比如 demo_1234.mp4 的转码的文件。可以配置自己的规则,比如 {inputName}_fix.${ext},这样会生成 demo_fix.mp4,方便程序处理;
- 目标路径 - 转码后的保存路径,可以放在不同路径,方便查询获取,当然也可以保存在原始路径 ${inputPath};
- 转码模板 - 这里就是转码的核心地方,选择具体的转码格式,务必选择H.264视频编码 + AAC音频编码,这是浏览器原生支持的黄金组合,这里选择 H264-MP4-2K。
配置好后点击确定后返回,并保存,返回工作流列表:

这里点击 "上传触发执行",这样在存储桶有视频文件上传时,就会自动进行转码。
7.2、H5页面生成二维码图片
7.2.1、需求分析
直接发链接感觉有点low,发个图片在群里,让大家扫码跳转吧。
7.2.2、草料二维码
随便搜一个,这是AI推荐的第一个,所以借用一下。
官网地址:https://cli.im/url。

还可以点击上图中右方的"二维码美化":

点击上方的"更换样式",有更多花样:

选择喜欢的样式:

可以调整文字颜色等,确认ok后,点击"下载打印"即可。
