跳到主要内容

全景图生成VR

本教程将指导您使用开放平台接口,将一组全景图上传并生成可在线浏览的 VR 空间。整个流程包括:获取访问令牌、获取上传凭证、上传全景图 ZIP 包、提交处理任务、轮询任务状态,最终获取 VR 链接。

前置准备

  • 已在如视开放平台注册账号并获取 app_keyapp_secret
  • 已申请开通「全景图生成 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.jpg

manifest.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 }
  ]
}

字段说明

字段类型说明
versionstring数据包版本号,当前固定为 "1.0"
floor_mapobject楼层映射。key 为 scan_list 中的 floor 值(字符串),value 为实际楼层编号
project_namestring项目名称。支持 {{timestamp}} 占位符自动替换为当前时间戳
scan_listarray全景图列表
scan_list[].idstring全景图标识,必须与 images/ 目录下的文件名(不含扩展名)一致
scan_list[].floornumber该全景图所在楼层,对应 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_keystring开放平台的 AppKey
app_secretstring开放平台的 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_tokenstring访问令牌,用于后续接口的 Authorization 请求头
expire_atinteger过期时间(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
  }
}

关键字段说明

字段类型说明
tmpSecretIdstring临时 AccessKeyId
tmpSecretKeystring临时 SecretAccessKey
sessionTokenstring临时会话 Token
ttlinteger凭证有效期(秒),通常为 3600
prefixstring上传路径前缀,上传 key 须拼接在此之后
bucketstring存储桶名称
regionstring存储桶区域
hoststring上传 endpoint
is_acceleratestring是否开启传输加速:"1" 开启,"0" 关闭
download_typestring下载方式:presign(预签名/私有桶)或 direct(直链/公开桶)
download_hoststring下载专用 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_namestring项目名称,用于创建 project
zip_cos_urlstringZIP 包的 HTTP 公网下载地址(支持预签名 URL),与 private_cos_key 二选一
private_cos_keystringZIP 包在私有桶中的对象路径(= 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_codestring任务唯一标识,用于后续查询任务状态
project_idstring创建的项目 ID

常见错误

codestatus说明
-1zip_cos_url or private_cos_key is required缺少文件路径参数
-1create project via mix-svc failed: 用户标识不能为空access_token 鉴权失败或权限不足
-3access_token expiredtoken 过期,需重新获取

步骤五:查询任务状态

使用 task_code 轮询任务处理状态,直到任务完成。

  • 接口: GET /open/v1/pano/task/status
  • Header: Authorization: {access_token}

请求参数

参数类型必填说明
task_codestring步骤四返回的任务 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_idstring请求唯一 ID
trace_idstring链路追踪 ID
codeinteger响应码:0 成功,-1 业务失败,-3 access_token 过期
statusstring状态描述
dataobject/null业务数据,失败时为 null
costinteger服务端处理耗时(ms)

常见问题

access_token 过期怎么办?

access_token 有有效期(通过 expire_at 字段标识)。当接口返回 code: -3 时,需重新调用 /auth/access_token 获取新 token。建议在轮询任务状态时加入自动续期逻辑。

上传凭证过期怎么办?

上传凭证有效期为 3600 秒(1 小时)。如果上传耗时较长导致凭证过期,需重新调用获取凭证接口。

任务一直处于 processing 状态?

全景图转 VR 处理通常需要几分钟时间,建议:

  • 轮询间隔设置为 5 秒
  • 设置最大轮询时间(如 10 分钟),超时后提示用户稍后使用 task_code 重新查询