跳到主要内容

使用指南

云开发 2.0 PaaS 自定义模式 提供了底层资源无关的云存储、云数据库、云函数调用能力和通用 HTTP 服务转发功能,借助它,云开发可以与自有 IaaS 配合使用,满足更丰富的定制化场景。本指南将提供上手云函数、HTTP 服务转发、云数据库 CRUD 和 云存储文件访问 的基础指引。

准备工作

要使用云开发自定义模式,需要先在腾讯云控制台开通自定义模式环境,并连接需要使用的资源(云数据库、云函数、服务提供方等)。

注意:自定义模式集成了云开发 IDaaS 登录体系,需要完成登录(即拥有登录态的用户)才能调用服务,所以需要配合云开发 IDaaS 能力使用。 这需要从 云开发 IDaaS 身份认证服务获取用于调用 OpenAPI 的 AccessToken,并在调用时通过请求头 Authorization 发送。

以下为通过 js-sdk 的方式,完成登录流程,并在请求时,自动携带登录相关信息。

import { CloudbaseOAuth } from "@cloudbase/oauth";
import * as tcbpaas from "@cloudbase/paas-js-sdk";

// 初始化 CloudbaseOAuth 实例
const oauth = new CloudbaseOAuth({
apiOrigin: "https://{env}.ap-shanghai.auth.tcloudbase.com",
clientId: "your-client-id", // 通过控制台获取
});

// 初始化 tcbpaas sdk 实例
tcbpaas.init({
env: "your-env-id",
baseURL: "https://{env}.ap-shanghai.api.tcloudbase.com",
oauthInstance: oauth,
});

// 按需进行匿名登录
async function signInAnonymously() {
const result = await oauth.authApi.hasLoginStateSync();
if (!result) {
await oauth.authApi.signInAnonymously();
}
}

signInAnonymously();

async function r(options) {
await signInAnonymously();
// 调用API
return tcbpaas.request(options);
}

本指南后续将使用更通用的 curlNode.js 进行 API 调用演示。

服务提供方

服务提供方可选云函数(SCF)和自定义 HTTP 服务,其调用地址均为:

https://{env}.ap-shanghai.gateway.tcloudbase.com/

其中 env 为云开发 PaaS 环境 ID,可以在云开发控制台进行查看。

调用云函数 SCF

在调用云函数之前,需要先在控制台绑定对应的云函数命名空间,绑定的命名空间下所有云函数即可被调用。例如: scf 云函数可以在Serverless 函数服务控制台进行创建与更新。

以下是一个简单的云函数 echo,它将返回请求的触发事件数据 event

exports.main_handler = async (event, context) => {
return event;
};

调用该云函数,需在请求 Query 参数 fn 指定要访问的函数名称。

使用 curl 调用:

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-H "X-RequestId: 783c4a34-73d4-11ed-b363-38f3ab16981b" \
-d '{ "key": "value" }' \
"https://{env}.ap-shanghai.gateway.tcloudbase.com/path/to/target?fn=echo&k=v"

云函数接收到的事件触发信息 event 内容如下:

{
// body 请求体,对应 http 请求 body
"body": {
"key": "value"
},
// header 请求头,对应 http 请求 header
"header": {
"Accept": ["*/*"],
"Authorization": ["Bearer {{accesstoken}}"],
"Content-Length": ["22"],
"Content-Type": ["application/json"],
"Traceparent": [
// 用于 http trace 的 header
"00-81d2afea14efe195a4aa8d12ebb30fa7-f2a8bb8bc673a1f1-01"
],
"Tracestate": [
// 用于 http trace 的 header
"tcb-paas-openapi=tracer"
],
"User-Agent": ["curl/7.68.0"]
},
"method": "POST",
"path": "/path/to/target",
"query": "fn=echo&k=v",
// 请求Id
"requestId": "783c4a34-73d4-11ed-b363-38f3ab16981b",
// tcb 请求上下文
"tcbContext": {
"accessToken": "Bearer {{accesstoken}}",
"appId": "1********", // 腾讯云账号AppId
"envId": "{{env}}", // PaaS 环境 ID
"uin": "100*******", // 腾讯云账号Uin
"userId": "1*****",
"userScope": ""
}
}

调用时的请求体会被置于 body 中。

上面的请求指定了 Content-Type: application/json,如果不指定该字段,云函数收到的 event 将会是:

{
"body": "{ \"key\": \"value\" }"
}

指定 Content-Type 内容类型为 application/jsonapplication/ld+json 时,服务端会将解析后的 JSON 放到 eventbody 字段上。

此外,如果传递了不合法的 JSON 格式数据,即使声明了 Content-Type: application/json,云函数也将接收到未解析的 JSON。例如如下调用:

curl -X POST \
-H "Authorization: {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '{ "key": "value }' \
"https://{env}.ap-shanghai.gateway.tcloudbase.com?fn=echo"

云函数接收到的 body 内容为:

{
"body": "{ \"key\": \"value }"
}

实例:利用云函数压缩 JPG 图片

这个例子将提供一个更真实的案例:通过云开发 PaaS OpenAPI 调用提供了压缩图片功能的云函数,并得到压缩后的图片。

首先是云函数部分。在这个名为compress的云函数中,实现对 JPG 图片进行压缩的功能:

云函数代码:

// 云函数入口文件 index.js
const images = require("images");

exports.main_handler = async (event, context) => {
// 从请求体中读取 base64 格式的图片
const imageBuf = event["body"] && Buffer.from(event["body"], "base64");

let result = {
message: "image not found",
code: "NOT_FOUND",
};

if (imageBuf) {
const originalSize = imageBuf.length;
const start = Date.now();
const compressedBuf = compress(imageBuf);
const timeCost = Date.now() - start;
const compressSize = compressedBuf.length;
const ratio = (compressSize / originalSize) * 100;
// 对图片进行压缩,并返回压缩后的图片、压缩时间和压缩比率
result = {
...result,
message: "compress success",
code: "SUCCESS",
compressedBuf,
ratio,
timeCost,
};
}

return result;
};

function compress(imageBuf) {
return images(imageBuf).encode("jpg", { quality: 20 });
}

客户端请求代码:

// 调用 PaaS OpenAPI 的参数:
const fnName = "compress";
const envId = "{{env}}";
const host = `${envId}.ap-shanghai.gateway.tcloudbase.com`;
const authorization = "Bearer {{accesstoken}}";
const url = `https://${host}/path/to/target?fn=${fnName}`;

// 获取一张随机图片,并将之转码为 `base64` 格式:
const resp = await fetch("https://picsum.photos/200", { method: "GET" });
const imageBuf = await resp.arrayBuffer();
const image = Buffer.from(imageBuf).toString("base64");

// 调用 OpenAPI 得到压缩后的图片,并将之写入本地文件:
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Host": host,
Authorization: authorization,
},
body: image,
})
.then((res) => res.json())
.then((res) => {
// 打印返回结果
console.log(res);
// 将图片保存至本地
const data = Buffer.from(res.compressedBuf, "base64");
fs.writeFileSync("compressed.jpg", data);
});

调用成功的返回结果如下:

{
"code": "SUCCESS",
"compressedBuf": {
"data": [
255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1 ...
],
"type": "Buffer"
},
"message": "compress success",
"ratio": 44.78914202617547,
"timeCost": 2
}

并能可以在本地看到压缩后的随机图片。

调用 HTTP 服务

在调用 HTTP 服务之前,需要先在控制台配置 HTTP 服务的转发地址,例如:

http-service如图将服务地址转发至 https://httpbin.org,这是一个用于获取各类 HTTP 响应的服务,后面将使用它来获取一段 HTTP JSON 响应。

与调用云函数类似,Authorization 请求头是必要的,另外,调用的路径应被拼接在请求路径之后。

例如,此处需要调用 https://httpbin.org/json ,需要把 /json 拼接在原有的调用路径之后。

注意:HTTP 服务提供方仅支持 VPC内网服务

curl -H "Authorization: {{accesstoken}}" \
-H "Content-Type: application/json" \
"https://{env}.ap-shanghai.gateway.tcloudbase.com/json"

将会返回如下结果:

{
"slideshow": {
"author": "Yours Truly",
"date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
{
"items": [
"Why <em>WonderWidgets</em> are great",
"Who <em>buys</em> WonderWidgets"
],
"title": "Overview",
"type": "all"
}
],
"title": "Sample Slide Show"
}
}

云数据库

在调用云数据库 API 之前,需要先在控制台绑定对应云数据库(当前仅支持腾讯云 MongoDB)。

通过调用 PaaS OpenAPI,可以对多文档或单文档进行查询、插入、更新、删除、聚合、计数等操作,对于每一个集合,都可以在控制台「集合配置」界面设置不同的读写策略来保证数据安全。

在执行每种操作时,可以通过 Query 参数 mode 指定使用 JSONExtJSON 格式操作文档。

本指南不会涉及有关 MongoDB 的基础概念和详细的 API 说明,仅采用 mode=JSON 格式数据进行 API 调用演示,有关具体 API 定义,请参考API 文档

数据库的调用地址格式为:

https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/{operate}?mode=JSON

其中:

  1. env 为云开发 PaaS 环境 ID,可以在云开发控制台进行查看。
  2. collectionName 为需要操作的数据库集合名。
  3. operate 为对应的操作
  4. mode 为文档数据格式,JSON | ExtJSON

插入文档 Insert

POST https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/insert?mode=JSON

在请求体的 docs 参数中填写需要插入的文档:

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '{ "docs": [{"id": 1, "a": 1}, {"id": 2, "a": 2}] }' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/insert?mode=JSON"

该请求插入了如下的两条文档:

db-insert

返回值指示了插入文档的数量及 ID

{
"inserted": 2,
"insertedIds": [
"09a2b1ab638d9fb90000006b5bd0a3f1",
"09a2b1ab638d9fb90000006c4ce1a2bc"
]
}

查询文档 Query

POST https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/query?mode=JSON

在请求体的 query 参数中填写查询条件,其他可指定的参数有:

  • projection: 返回字段,1 表示返回,-1 表示不返回,如:a:1,b:1,c.d.e:1,_id:-1,则返回 abc.d.e
  • sort: 排序字段,1 表示正序,-1 表示倒序,排序字段顺序决定排序次序,如:sales:1,则按sales正序排序
  • offset: 偏移量
  • limit: 限制数量
curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '{ "query": {"id": 1} }' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/query?mode=JSON"

该请求查询 id=1 的文档,返回值如下所示:

{
"offset": 0,
"limit": 0,
"docs": [
{
"_id": "09a2b1ab638d9fb90000006b5bd0a3f1",
"_openid": "1563083521218580480",
"a": 1,
"id": 1
}
]
}

返回值包含了查询所得的文档列表 docs 、请求传入的 offsetlimit

更新文档 Update

POST https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/update?mode=JSON

在请求体的 query 参数中填写查询条件,update 中填写更新规则,其他可指定字段有:

  • upsert: boolean 无匹配文档时是否插入文档,默认 false
  • multi: boolean 是否更新多条数据,默认 false
  • isReturnNewDoc: boolean 是否返回更新后的文档,默认 false

注意,isReturnNewDocmulti 不能同时为 trueisReturnNewDocupsert 也不能同时为 true

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '{ "query": {"id": 1}, "update": {"$set": { "a": 100 }} }' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/update?mode=JSON"

该请求将 id=1 的文档的 a 字段修改为 100。返回值如下:

{
"matched": 1, // 匹配的文档数量
"modified": 1, // 更新的文档数据
"upsertId": "", // 更新的文档 Id
"newDoc": "" // 如果 isReturnNewDoc=true ,则该字段返回更新后的文档
}

文档计数 Count

POST https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/count?mode=JSON

在请求体的 query 参数中填写查询条件。

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '{ "query": {"a": {"$gt": 50}} }' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/count?mode=JSON"

该请求查询字段 a>50 的文档数量,返回值指示了符合条件的查询结果:

{
"count": 1
}

查询条件中的 $gtMongoDB 操作符

聚合文档 Aggregate

POST https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/aggregate?mode=JSON

在请求体的pipeline参数中填写聚合管道。

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '{"pipeline":[{"$match":{"a": 100}},{"$group":{"_id":"a"}}]}' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/aggregate?mode=JSON"

该请求选出所有 a=100 的文档并按 a 分组。返回值示意:

{ "results": [{ "_id": "a" }] }

删除文档 Delete

POST https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/delete?mode=JSON

在请求体的 query 参数中填写查询条件,使用 multi 指定是否更新多条数据,默认为 false

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '{ "query": {"a": {"$gt": 50}} }' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/databases/-/collections/{collectionName}/delete?mode=JSON"

该请求删除了 a>50 的文档,返回值:

{ "removed": 1 }

removed 指示了匹配并删除的文档数量。

实例:操作 books 集合

下面将演示在一个更接近真实数据的集合 books 上进行增删改查、计数和聚合。首先设定请求的通用参数:

const envId = "{{env}}";
const host = `${envId}.ap-shanghai.api.tcloudbase.com`;
const authorization = "Bearer {{accesstoken}}";
const collectionName = "books";
const method = "POST";
const headers = {
Authorization: authorization,
"X-Host": host,
"Content-Type": "application/json",
};
const baseURL = `https://${host}/v1/databases/-/collections/${collectionName}`;

首先,在这个空集合中添加一些书籍,包含了书名、作者、价格、销量等信息:

async function InsertBooks() {
const books = [
{
id: 1,
name: "The Garden of Forking Paths",
sales: 45,
category: "fiction",
author: {
name: "Jorge Luis Borges",
age: 80,
},
onSale: true,
price: 10.5,
},
{
id: 2,
name: "The Outsider",
sales: 37,
category: "fiction",
author: {
name: "Albert Camus",
age: 46,
},
onSale: false,
price: 29.99,
tags: ["philosophy", "existentialism"],
},
{
id: 3,
name: "Duino Elegies",
sales: 20,
category: "poetry",
author: {
name: "Rainer Maria Rilke",
age: 42,
},
onSale: true,
price: 15.99,
tags: ["poetry", "philosophy"],
},
{
id: 4,
name: "The Red and the Black",
sales: 30,
category: "fiction",
author: {
name: "Stendhal",
age: 35,
},
onSale: true,
price: 15.99,
},
];

const url = `${baseURL}/insert?mode=JSON`;
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify({ docs: books }),
});

const data = await resp.json();
console.log(data);
}

成功插入了 4 条数据:

{
"inserted": 4,
"insertedIds": [
"09a2b1ab638db2700000008354aa6500",
"09a2b1ab638db27000000084374ffb24",
"09a2b1ab638db270000000851cdb3f4e",
"09a2b1ab638db270000000861164410d"
]
}

接着,查询销量大于 30(sales>30)的书籍,并按降序排列,指定只需要返回书名 name、销量 sales 和 书籍分类 category 字段:

async function QueryBooks() {
const params = {
query: {
sales: { $gt: 30 },
},
sort: "sales:-1",
projection: "name:1,sales:1,category:1",
};

const url = `${baseURL}/query?mode=JSON`;
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify(params),
});

const data = await resp.json();
console.log(data);
}

调用函数后,可以得到符合条件并按销量降序排列的书籍列表:

{
"offset": 0,
"limit": 0,
"docs": [
{
"_id": "09a2b1ab638db2700000008354aa6500",
"category": "fiction",
"name": "The Garden of Forking Paths",
"sales": 45
},
{
"_id": "09a2b1ab638db27000000084374ffb24",
"category": "fiction",
"name": "The Outsider",
"sales": 37
}
]
}

接下来,对书籍的销量进行更新,将所有销量小于 50(sales<50)的书籍销量都更新为 50:

async function UpdateBooks() {
const params = {
query: {
sales: { $lt: 50 },
},
update: { $set: { sales: 50 } },
multi: true,
};

const url = `${baseURL}/update?mode=JSON`;
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify(params),
});

const data = await resp.json();
console.log(data);
}

从返回值上,可以看到所有的书籍销量都被更新了:

{ "matched": 4, "modified": 4, "upsertId": "", "newDoc": "" }

为了确认现在书籍的销量都大于 50,使用计数功能进行统计:

async function CountBooks() {
const params = {
query: {
sales: { $gte: 50 },
},
};

const url = `${baseURL}/count?mode=JSON`;
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify(params),
});

const data = await resp.json();
console.log(data);
}

返回值表明前面的操作结果符合预期:

{ "count": 4 }

下面,利用聚合管道统计出价格不高于 20(price<20)的小说(fiction),并按降序排列,仅返回指定字段:

async function AggregateBooks() {
const params = {
pipeline: [
{
$project: {
name: "$name",
category: "$category",
canAfford: { $lte: ["$price", 20] },
price: "$price",
},
},
{ $match: { $and: [{ category: "fiction" }, { canAfford: true }] } },
{ $sort: { price: 1 } },
],
};

const url = `${baseURL}/aggregate?mode=JSON`;
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify(params),
});

const data = await resp.json();
console.log(data);
}

result 字段中,可以看到聚合结果:

{
"results": [
{
"_id": "09a2b1ab638db2700000008354aa6500",
"canAfford": true,
"category": "fiction",
"name": "The Garden of Forking Paths",
"price": 10.5
},
{
"_id": "09a2b1ab638db270000000861164410d",
"canAfford": true,
"category": "fiction",
"name": "The Red and the Black",
"price": 15.99
}
]
}

最后,删除销量大于等于 50(sales>=50)的文档,即清空数据:

async function DeleteBooks() {
const params = {
query: {
sales: { $gte: 50 },
},
multi: true,
};

const url = `${baseURL}/delete?mode=JSON`;
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify(params),
});
const data = await resp.json();
console.log(data);
}

返回值:

{ "removed": 4 }

books 集合中所有的数据都被删除了。

云存储

在使用云存储之前,需要先在控制台配置对象存储提供方(当前仅支持腾讯云 COS)。PaaS 云存储 OpenAPI 提供了以下能力:

  • 获取对象上传信息
  • 获取对象下载信息
  • 删除对象

也可以在控制台配置对象存储桶的访问权限或设置缓存。

获取对象上传信息

POST https://{env}.ap-shanghai.api.tcloudbase.com/v1/storages/get-objects-upload-info

请求体是一个 JSON 数组,在请求体的 ObjectId 中写入对象 ID(如:文件名),示例:

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '[{"objectId":"image.png"}]' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/storages/get-objects-upload-info"

返回值示例:

[
{
"uploadUrl": "https://cos.ap-shanghai.myqcloud.com/****/image.png", // 上传地址
"downloadUrl": "https://{{env}}-****.tcb.qcloud.la/image.png?sign=04fe912d9f04ed363e641f6590379e94/image.png1670238551\u0026t=1670238551", // 下载地址
"token": "8Blmgy...", // 上传 token,上传到 COS 时携带在 HTTP Header 中
"authorization": "q-sign-algorithm=...", // 上传 authorization,上传到 COS 时携带在 HTTP Header 中
"cloudObjectMeta": "VENCQ2xvdWRPYmplY3RNZXRhLnYxLnRlc3QtMTI1OD...", // 上传元数据信息
"cloudObjectId": "cloud://{{env}}.****/image.png", // 云端对象 ID
"objectId": "image.png" // 传入的对象 ID
}
]

如果获取上传信息出错,将会通过错误码(code)和错误信息(message)信息返回,例如:

[
{
"code": "xxx",
"message": "yyy"
}
]

此外,上传时还可以指定 signedHeader 添加额外的签名头部字段,例如添加 MD5:

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '[{"objectId":"image.png","signedHeader":{"content-md5":["{{your-md5}}"]}}]' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/storages/get-objects-upload-info"

COS 将会校验 MD5 以保证对象完整性。

MD5 计算方式请查看:COS 相关文档

获取到文件上传信息后,可通过该信息,将文件上传到 COS 对象存储中。

axios.put(uploadUrl, file, {
headers: {
Authorization: authorization,
"x-cos-meta-fileid": cloudObjectMeta,
"x-cos-security-token": token,
"Content-Type": "image/png",
},
});

注意:浏览器中上传,需在 COS-安全管理-跨域访问 CORS 设置 添加跨域访问规则。

获取对象下载信息

请求方法:POST,请求地址:https://{env}.ap-shanghai.api.tcloudbase.com/v1/storages/get-objects-download-info

请求体是一个 JSON 数组,在请求体的CloudObjectId中写入云端对象 ID,并可通过maxAge指定对象有效期。示例:

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '[{"cloudObjectId":"cloud://{{env}}.{{cosInstance}}/image.png","maxAge":120},{"cloudObjectId":"cloud://{{env}}.{{cosInstance}}/404.txt"}]' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/storages/get-objects-download-info"

返回值:

[
{
"cloudObjectId": "cloud://{{env}}.{{cosInstance}}/test/404.txt", // 传入的云端对象 ID
"code": "OBJECT_NOT_EXIST", // 错误码
"message": "Storage object not exists" // 错误描述
},
{
"cloudObjectId": "cloud://{{env}}.{{cosInstance}}/video-icon.png",
"downloadUrl": "https://..." // 下载地址
}
]

其中,对于不存在的文件,将返回 OBJECT_NOT_EXIST 的错误码,且 downloadUrl 置空,如文件存在,则通过 downloadUrl 返回下载地址。

删除对象

POST https://{env}.ap-shanghai.api.tcloudbase.com/v1/storages/delete-objects

请求体是一个 JSON 数组,在请求体的 CloudObjectId 中写入云端对象 ID,示例:

curl -X POST \
-H "Authorization: Bearer {{accesstoken}}" \
-H "Content-Type: application/json" \
-d '[{"cloudObjectId":"cloud://{{env}}.{{cosInstance}}/image.png"}]' \
"https://{env}.ap-shanghai.api.tcloudbase.com/v1/storages/delete-objects"

返回值示例:

[
{
"cloudObjectId": "cloud://{{env}}.{{cosInstance}}/image.png" // 传入的 CloudObjectId
}
]

如果文件不存在,将会收到如下的错误码和相应的错误信息:

[
{
"cloudObjectId": "cloud://{{env}}.{{cosInstance}}/not-exist.png",
"code": "OBJECT_NOT_EXIST",
"message": "Storage object not exists"
}
]

实例:上传、下载和删除 COS 中的图片

下面将演示一个更接近实际的例子:在 Node.js 环境中调用云开发 PaaS OpenAPI 进行图片的上传、下载并保存到本地和删除。代码运行环境为 Node.js v16.16.0。

首先,导入使用到的库:

import fetch from "node-fetch";
import fs from "fs";
import path from "path";
import { fileTypeFromBuffer } from "file-type"; // 用于判断文件类型 https://www.npmjs.com/package/file-type

并定义发送请求的通用变量:

const envId = "{{env}}";
const host = `${envId}.ap-shanghai.api.tcloudbase.com`;
const baseURL = `https://${host}/v1/storages`;
const method = "POST";
const headers = {
"X-Host": host,
"Content-Type": "application/json",
Authorization: "Bearer {{accesstoken}}",
};

假设在当前目录下有一张图片 kumanon.jpg,接下来需要将它上传到 COS,再从 COS 中下载并保存它的副本,最后删除它。第一步,实现用于获取文件上传信息的通用函数:

/**
*
* @param {string} filename 文件名
* @returns 文件上传信息
*/
async function getFileUploadInfo(filename) {
const param = [{ objectId: filename }];
// 获取对象上传信息的请求地址
const url = `${baseURL}/get-objects-upload-info`;
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify(param),
});
const data = await resp.json();
return data;
}

接着,调用它获取上传信息,并完成上传图片到 COS 的操作:

/**
*
* @param {string} filepath 文件路径
*/
async function uploadFileToCOS(filepath) {
// 获取文件上传信息
const filename = path.basename(filepath);
const data = await getFileUploadInfo(filename);
// 从返回值中解构需要用到的变量
const { authorization, cloudObjectMeta, token, uploadUrl } = data[0];

// 读取图片并获取其 mime 类型
const file = fs.readFileSync(filepath);
const { mime } = await fileTypeFromBuffer(file);

// 上传文件到 COS
const resp = await fetch(uploadUrl, {
method: "PUT",
headers: {
"Content-Type": mime,
Authorization: authorization,
"x-cos-meta-fileid": cloudObjectMeta,
"x-cos-security-token": token,
},
body: file,
});
// 打印上传结果
const respCode = resp.status;
if (respCode !== 200) {
const reason = await resp.text();
console.log(reason);
}
console.log(`上传文件 ${filename} ${respCode === 200 ? "成功" : "失败"}`);
}

调用该函数:

uploadFileToCOS("kumanon.jpg");

可以在云开发控制台或者 COS 控制台看到对应图片已被上传:

cos-upload

并可以预览图片:

cos-preview

接下来,实现一个函数用于获取文件下载信息:

/**
*
* @param {string} cloudObjectId 云端文件 ID
* @returns 文件下载信息
*/
async function getFileDownloadInfo(cloudObjectId) {
const param = [{ cloudObjectId, maxAge: 120 }];
// 获取对象下载信息的请求地址
const url = `${baseURL}/get-objects-download-info`;
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify(param),
});
const data = await resp.json();
return data;
}

从云开发控制台复制云端对象 ID(cloudObjectId),并用它来下载刚刚上传的 kumanon.jpg 并保存到本地:

/**
*
* @param {string} cloudObjectId 云端文件 ID
*/
async function downloadFileFromCOS(cloudObjectId) {
// 获取文件下载地址
const data = await getFileDownloadInfo(cloudObjectId);
const { downloadUrl } = data[0];
const url = encodeURI(downloadUrl);
// 下载文件
const resp = await fetch(url, {
method: "GET",
});

// 获取文件名和文件类型,并将图片保存到本地
const basename = path.basename(url).split("?")[0].split(".")[0];
const timestamp = new Date().getTime();
const file = await resp.arrayBuffer();
const { ext } = await fileTypeFromBuffer(file);
fs.writeFileSync(`${basename}${timestamp}.${ext}`, Buffer.from(file));
}

调用该函数:

downloadFileFromCOS("cloud://{{env}}.{{cosInstance}}/kumanon.jpg");

可以看到图片被下载并以 kumanon${timestamp}.jpg 的名称保存到了当前目录下。

最后,删除这张图片:

/**
*
* @param {string} cloudObjectId 云端文件 ID
*/
async function deleteObject(cloudObjectId) {
const param = [{ cloudObjectId }];
// 删除对象的请求地址
const url = `${baseURL}/delete-objects`;
// 删除对象
const resp = await fetch(url, {
method,
headers,
body: JSON.stringify(param),
});
// 打印删除结果
const data = await resp.json();
if (data[0].code) {
console.log(`删除文件 ${cloudObjectId} 失败`);
console.log("错误信息:", data[0].message);
} else {
console.log(`删除文件 ${cloudObjectId} 成功`);
}
}

调用该函数,将会删除 COS 中的 kumamon.jpg