键值数据存储 (KV)
分类代号:C.8 本章共 5 个接口
vjmap 内置了一个按工作区隔离的 KV 数据表(SQLite),用来存用户配置、缩略图、缓存数据等零碎东西。"零碎"并不代表"不好用"——二开里非常常用,比如存每个用户的图层可见性、自定义地图标注等。
存储位置:后端 data/customData.db。
自动前缀:所有用户可见 key 都加了隐形 data_ 前缀,本 SDK 已屏蔽此细节。
TTL:可选过期时间(秒),到期自动清理。
批量 key 分隔符:路径里 ;;(双分号)分隔多个 key。
快速索引
| 序号 | SDK 方法 | URL |
|---|---|---|
| 53 | get_custom_data / getCustomData |
GET /api/v1/service/data/{key} |
| 54 | save_custom_data / saveCustomData |
POST /api/v1/service/data/{key} |
| 55 | delete_custom_data / deleteCustomData |
DELETE /api/v1/service/data/{key} |
| 56 | save_multiple_custom_data / saveMultipleCustomData |
POST /api/v1/service/datas |
| 57 | get_custom_data_keys / getCustomDataKeys |
GET /api/v1/service/datakeys?prefix= |
53 · 获取值 data/{key} GET
查询参数
| 字段 | 取值 | 说明 |
|---|---|---|
retDataType |
"" / "value" / "prop" |
默认返回 {data, prop};value 只要 data;prop 只要 prop |
contentType |
"" / "image" |
image 时后端把 base64 dataURL 解出,直接返回 PNG 二进制(不是 JSON) |
批量:key 路径段用 ;; 拼接多 key,SDK 传字符串列表即可。
响应
- 存在:
{ "status": "ok", "data": <value>, "prop": <prop> } - 不存在:
{ "status": "keyNoExist" }(不是 404) - 批量:
{ "values": [...], "props": [...], "status": [...] }
实采
// get_custom_data.json
{
"data": "hello-vjmap",
"prop": "text/plain",
"status": "ok"
}
// get_missing.json —— 一定要判断 status
{ "status": "keyNoExist" }
Python
d = svc.get_custom_data("myproj/theme")
if d["status"] == "keyNoExist":
...
else:
theme = d["data"]
Node.js
const d = await svc.getCustomData('myproj/theme');
if (d.status === 'keyNoExist') { /* ... */ }
批量读
svc.get_custom_data(["a", "b"])
# { "values": ["va","vb"], "props":["pa","pb"], "status":["ok","ok"] }
54 · 保存值 data/{key} POST
查询参数
| 字段 | 说明 |
|---|---|
ttl |
秒;到点清除。省略或 0 = 长期 |
Body 两种写法
- JSON:
{"value": "...", "prop": "..."};prop可存元数据(MIME、扩展字段) - Raw:直接把文件二进制 POST 上去,作为 value 字段存(SDK 里叫
raw_body/rawBody)
响应(实采)
{
"data": "nb_kv_a_1776832185",
"status": "ok"
}
Python
svc.save_custom_data("myproj/theme",
value='{"mode":"dark"}',
prop="application/json")
# 带 TTL
svc.save_custom_data("tmp_token",
value="eyJ...", ttl=3600)
# Raw bytes
svc.save_custom_data("user_avatar_42",
raw_body=open("avatar.png","rb").read())
Node.js
await svc.saveCustomData('myproj/theme', {
value: JSON.stringify({ mode: 'dark' }),
prop: 'application/json',
});
await svc.saveCustomData('tmp_token', { value: tokenStr, ttl: 3600 });
55 · 删除值 data/{key} DELETE
查询参数
| 字段 | 说明 |
|---|---|
isPrefix |
"true" 时把 key 当前缀,批量清除;否则只删单条 |
响应
- 单条(实采):
{"data":"nb_kv_a_...","status":"ok"} - 前缀(实采):
{"data":"nb_prefix_...","isPrefix":true,"status":"ok"}
Python
svc.delete_custom_data("myproj/theme")
# 前缀批量
svc.delete_custom_data("myproj/user42/", is_prefix=True)
# 列表批量(走 ;; 分隔)
svc.delete_custom_data(["a", "b", "c"])
56 · 批量存值 datas POST
URL:POST /api/v1/service/datas
Body
[{"key":..,"value":..,"prop":..,"ttl":"60"}],注意 ttl 是字符串秒数。
响应(实采)
{ "status": ["ok"] }
Python
svc.save_multiple_custom_data([
{"key": "cfg/a", "value": "1"},
{"key": "cfg/b", "value": "2", "ttl": "3600"},
])
Node.js
await svc.saveMultipleCustomData([
{ key: 'cfg/a', value: '1' },
{ key: 'cfg/b', value: '2', ttl: '3600' },
]);
57 · 按前缀列键 datakeys GET
查询参数
| 字段 | 说明 |
|---|---|
prefix |
前缀(自动加 data_) |
retDataType |
value / prop / valueprop —— 同时取出值,省一次往返 |
isSys |
true 时返回带 data_ 内部前缀的系统键(仅 root 场景) |
响应
- 仅 keys(实采):
{ "keys": ["nb_kv_a_1776832185", "nb_kv_b_1776832185"] }
- 带值(
retDataType=valueprop,实采):
{
"keys": ["nb_kv_a_1776832185", "nb_kv_b_1776832185"],
"props": ["text/plain", "bp-1"],
"values": ["hello-vjmap", "batch-1"],
"status": ["ok", "ok"]
}
Python
keys = svc.get_custom_data_keys(prefix="myproj/")["keys"]
# 同时取值
bundle = svc.get_custom_data_keys(prefix="myproj/", ret_data_type="valueprop")
for k, v, p in zip(bundle["keys"], bundle["values"], bundle["props"]):
print(k, v, p)
最佳实践
- 前缀分层:用
//:建立命名空间,如myproj/user42/tool_state。这样前缀批量删除、列出都方便。 - 大文件不要存 KV:value 单条一般 < 1 MB;大图/DWG 用
/map/uploadmap或独立对象存储。 - TTL 清理:用
ttl让后端帮忙过期;别自己写 crontab。 keyNoExist≠ 错误:200 正常响应里status == "keyNoExist"表示没值,而不是失败。
这篇文档有帮助吗?感谢反馈!