全景图生成VR
本教程将指导您使用开放平台接口,将一组全景图上传并生成可在线浏览的 VR 空间。整个流程包括:获取访问令牌、获取上传凭证、上传全景图 ZIP 包、提交处理任务、轮询任务状态,最终获取 VR 链接。
前置准备
- 已在如视开放平台注册账号并获取
app_key和app_secret- 国内用户:前往 my.realsee.cn 注册
- 海外用户:前往 my.realsee.ai 注册
- 已申请开通「全景图生成 VR」接口能力(发邮件至 developer@realsee.com,提供账号归属区、UserID、IdentityID)
- 准备好全景图文件(等距柱状投影格式的全景图,JPG 格式)
API 基础地址
| 环境 | 基础地址 |
|---|---|
| 生产环境 | https://app-gateway.realsee.cn |
数据包格式
全景图需打包为 ZIP 格式上传。ZIP 包内包含一个 manifest.json 配置文件和一个 images/ 图片目录:
my-project.zip
├── manifest.json
└── images/
├── entrance.jpg
├── entrance-02.jpg
├── end-of-nave.jpg
├── presbyterium.jpg
└── high-altar.jpgmanifest.json 示例
{
"version": "1.0",
"floor_map": {
"0": 1,
"1": 2
},
"project_name": "my-pano-project",
"scan_list": [
{ "id": "entrance", "floor": 0 },
{ "id": "entrance-02", "floor": 0 },
{ "id": "end-of-nave", "floor": 0 },
{ "id": "presbyterium", "floor": 1 },
{ "id": "high-altar", "floor": 1 }
]
}字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
version | string | 数据包版本号,当前固定为 "1.0" |
floor_map | object | 楼层映射。key 为 scan_list 中的 floor 值(字符串),value 为实际楼层编号 |
project_name | string | 项目名称。支持 {{timestamp}} 占位符自动替换为当前时间戳 |
scan_list | array | 全景图列表 |
scan_list[].id | string | 全景图标识,必须与 images/ 目录下的文件名(不含扩展名)一致 |
scan_list[].floor | number | 该全景图所在楼层,对应 floor_map 中的 key |
注意事项
images/目录下的图片文件名必须与scan_list中的id对应(如id为"entrance",则文件名为entrance.jpg)- 图片格式为 JPG,需为等距柱状投影(equirectangular)全景图
- 单楼层场景
floor_map只需一个映射即可,如{"0": 0}
流程概览
获取 access_token → 获取上传凭证 → 上传 ZIP 包 → 提交处理任务 → 轮询任务状态 → 获取 VR 链接步骤一:获取 access_token
所有业务接口都需要通过 access_token 鉴权。
- 接口:
POST /auth/access_token - Content-Type:
application/x-www-form-urlencoded
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_key | string | 是 | 开放平台的 AppKey |
app_secret | string | 是 | 开放平台的 AppSecret |
请求示例
const getAccessToken = async () => {
const res = await fetch('https://app-gateway.realsee.cn/auth/access_token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
app_key: 'your_app_key',
app_secret: 'your_app_secret',
}),
});
const data = await res.json();
return data.data;
};响应示例
{
"code": 0,
"status": "success",
"data": {
"access_token": "VE9LRU5fVjJLTmRBM3J1ZWVweEZwWVF6...",
"expire_at": 1710732700
}
}响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
access_token | string | 访问令牌,用于后续接口的 Authorization 请求头 |
expire_at | integer | 过期时间(Unix 时间戳,秒)。过期后需重新获取 |
错误码说明
code: 0— 成功code: -1, status: "illegal app"— app_key 或 app_secret 错误
步骤二:获取上传凭证
获取临时上传凭证,用于将全景图 ZIP 包上传到对象存储。
- 接口:
GET /open/v1/pano/file/token - Header:
Authorization: {access_token}
请求参数
无需传入参数。
请求示例
const getUploadToken = async (accessToken) => {
const res = await fetch('https://app-gateway.realsee.cn/open/v1/pano/file/token', {
headers: {
'Authorization': accessToken,
},
});
const data = await res.json();
return data.data;
};响应示例
{
"code": 0,
"status": "success",
"data": {
"tmpSecretId": "ASIASS...",
"tmpSecretKey": "...",
"sessionToken": "IQoJb3...",
"ttl": 3600,
"expire": 3600,
"prefix": "vrfile/release/open_task_original/T-XXXXXXXXXX/pano/1700000000/",
"app_id": "xxxxxxxxxxxx",
"bucket": "vrlab-private",
"region": "ap-northeast-1",
"host": "https://s3-accelerate.amazonaws.com",
"is_accelerate": "1",
"custom_domain": "vrlab-private.s3-accelerate.amazonaws.com",
"custom_scheme": "https",
"download_type": "presign",
"download_host": "",
"backup": null
}
}关键字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
tmpSecretId | string | 临时 AccessKeyId |
tmpSecretKey | string | 临时 SecretAccessKey |
sessionToken | string | 临时会话 Token |
ttl | integer | 凭证有效期(秒),通常为 3600 |
prefix | string | 上传路径前缀,上传 key 须拼接在此之后 |
bucket | string | 存储桶名称 |
region | string | 存储桶区域 |
host | string | 上传 endpoint |
is_accelerate | string | 是否开启传输加速:"1" 开启,"0" 关闭 |
download_type | string | 下载方式:presign(预签名/私有桶)或 direct(直链/公开桶) |
download_host | string | 下载专用 host。为空时表示私有桶,提交任务应使用 private_cos_key |
Token 过期
code: -3, status: "access_token expired"— access_token 已过期,需重新调用步骤一获取新 token
步骤三:上传全景图 ZIP 包
使用步骤二获取的临时凭证,通过 S3 SDK 将 ZIP 包上传到指定存储桶。
上传 key 拼接规则
上传 key = prefix + 文件名例如:vrfile/release/open_task_original/T-XXXXXXXXXX/pano/1700000000/my-project.zip
示例代码(Node.js + AWS S3 SDK)
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const fs = require('fs');
const uploadZip = async (token, zipFilePath) => {
const client = new S3Client({
region: token.region,
endpoint: `${token.custom_scheme}://${token.custom_domain}`,
credentials: {
accessKeyId: token.tmpSecretId,
secretAccessKey: token.tmpSecretKey,
sessionToken: token.sessionToken,
},
forcePathStyle: false,
});
const fileName = 'my-project.zip';
const key = `${token.prefix}${fileName}`;
await client.send(new PutObjectCommand({
Bucket: token.bucket,
Key: key,
Body: fs.createReadStream(zipFilePath),
}));
console.log('上传成功,key:', key);
return key;
};步骤四:提交处理任务
上传完成后,提交全景图转 VR 的处理任务。
- 接口:
POST /open/v1/pano/task/submit - Header:
Authorization: {access_token} - Content-Type:
application/json
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
project_name | string | 是 | 项目名称,用于创建 project |
zip_cos_url | string | 否 | ZIP 包的 HTTP 公网下载地址(支持预签名 URL),与 private_cos_key 二选一 |
private_cos_key | string | 否 | ZIP 包在私有桶中的对象路径(= prefix + 文件名),与 zip_cos_url 二选一 |
如何选择 zip_cos_url 和 private_cos_key
- 当步骤二返回的
download_host为空时(私有桶,download_type: "presign"):使用private_cos_key,值为上传时的完整 key - 当
download_host有值时:使用zip_cos_url,值为上传结果的 download_url
请求示例
const submitTask = async (accessToken, privateCosKey) => {
const res = await fetch('https://app-gateway.realsee.cn/open/v1/pano/task/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': accessToken,
},
body: JSON.stringify({
project_name: 'my-pano-project',
private_cos_key: privateCosKey,
}),
});
const data = await res.json();
return data.data;
};响应示例
{
"code": 0,
"status": "success",
"data": {
"task_code": "xxxxxxxxxxxxxxxxxxxx",
"project_id": "pano-xxxxxxxxxxxxxxxxxxxx"
}
}响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
task_code | string | 任务唯一标识,用于后续查询任务状态 |
project_id | string | 创建的项目 ID |
常见错误
| code | status | 说明 |
|---|---|---|
| -1 | zip_cos_url or private_cos_key is required | 缺少文件路径参数 |
| -1 | create project via mix-svc failed: 用户标识不能为空 | access_token 鉴权失败或权限不足 |
| -3 | access_token expired | token 过期,需重新获取 |
步骤五:查询任务状态
使用 task_code 轮询任务处理状态,直到任务完成。
- 接口:
GET /open/v1/pano/task/status - Header:
Authorization: {access_token}
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
task_code | string | 是 | 步骤四返回的任务 code(query 参数) |
请求示例
const pollTaskStatus = async (accessToken, taskCode) => {
const checkStatus = async () => {
const res = await fetch(
`https://app-gateway.realsee.cn/open/v1/pano/task/status?task_code=${taskCode}`,
{
headers: { 'Authorization': accessToken },
}
);
const data = await res.json();
// 处理 token 过期:自动续期
if (data.code === -3) {
throw new Error('access_token expired, please refresh');
}
return data.data;
};
return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
try {
const result = await checkStatus();
console.log('当前状态:', result.status);
if (result.status === 'success') {
clearInterval(interval);
console.log('VR 链接:', result.vr_url);
resolve(result);
} else if (result.status === 'failed') {
clearInterval(interval);
reject(new Error('任务处理失败'));
}
// status 为 pending 或 processing 时继续轮询
} catch (error) {
clearInterval(interval);
reject(error);
}
}, 5000); // 建议每 5 秒查询一次
});
};响应示例
处理中:
{
"code": 0,
"status": "success",
"data": {
"project_id": "pano-xxxxxxxxxxxxxxxxxxxx",
"status": "processing",
"vr_url": ""
}
}处理完成:
{
"code": 0,
"status": "success",
"data": {
"project_id": "pano-xxxxxxxxxxxxxxxxxxxx",
"status": "success",
"vr_url": "https://realsee.cn/link_code"
}
}任务状态枚举
| 状态值 | 说明 |
|---|---|
pending | 排队等待处理 |
processing | 处理中 |
success | 处理成功,vr_url 返回完整 VR 链接 |
failed | 处理失败 |
完整流程示例
const panoToVR = async () => {
// 步骤一:获取 access_token
const { access_token } = await getAccessToken();
// 步骤二:获取上传凭证
const token = await getUploadToken(access_token);
// 步骤三:上传 ZIP 包
const cosKey = await uploadZip(token, './my-project.zip');
// 步骤四:提交处理任务
const { task_code } = await submitTask(access_token, cosKey);
console.log('任务已提交,task_code:', task_code);
// 步骤五:轮询任务状态
const result = await pollTaskStatus(access_token, task_code);
console.log('处理完成,VR 链接:', result.vr_url);
return result.vr_url;
};
panoToVR().catch(console.error);通用响应结构
所有接口返回统一的响应信封:
| 字段 | 类型 | 说明 |
|---|---|---|
request_id | string | 请求唯一 ID |
trace_id | string | 链路追踪 ID |
code | integer | 响应码:0 成功,-1 业务失败,-3 access_token 过期 |
status | string | 状态描述 |
data | object/null | 业务数据,失败时为 null |
cost | integer | 服务端处理耗时(ms) |
常见问题
access_token 过期怎么办?
access_token 有有效期(通过 expire_at 字段标识)。当接口返回 code: -3 时,需重新调用 /auth/access_token 获取新 token。建议在轮询任务状态时加入自动续期逻辑。
上传凭证过期怎么办?
上传凭证有效期为 3600 秒(1 小时)。如果上传耗时较长导致凭证过期,需重新调用获取凭证接口。
任务一直处于 processing 状态?
全景图转 VR 处理通常需要几分钟时间,建议:
- 轮询间隔设置为 5 秒
- 设置最大轮询时间(如 10 分钟),超时后提示用户稍后使用
task_code重新查询