Krpano:打造全景漫游体验—全景开发(一)

前言

前面的文章主要基本介绍了krpano一些元素的用法,从这篇文章开始我们就进入全景开发的实战教学,从全景切图全景编辑最后到整个全景的预览,前端使用react框架+antd组件库+krpano,后端使用koa来实现一个类似720云全景的制作网站

image.png

image.png

之前的文章中都是通过MAKE VTOUR Droplet.exe应用程序来手动切图的,但是对于一个制作网站来说肯定是网站来帮用户实现切图,用户只需要上传全景图片,前端拿到图片再传给后端,通过后端来完成切图,所以步骤是-->用户选择图片切图-->客户端拿到图片后调用接口进行上传-->服务端实现切图-->切图完成

krpano实现切图demo

1.用户选择图片并调用接口上传

上传图片通过Upload组件上传,这里将官方文档的示例直接复制过来

<Upload {...props}>
  <Button type="primary">本地导入</Button>
</Upload>
const props: UploadProps = {
    name: 'panos',
    action: `${BASE_URL}/upload/panos`,
    headers: {
      authorization: localcache.getCacha('token')
    },
    showUploadList: false,
    multiple: true
}

2.实现切图

2.1如何实现切图

后端实现切图我们通过krpanotools.exe可执行文件执行命令行来切图,在我们从官网下来的包里就包含了这个文件

image.png

cmd执行一下krpanotools命令,可以看到这个应用程序提供好几个功能:

  1. makepano:制作基于模版的全景或漫游
  2. maketiles:制作或组合切片图像。可用于转换或重置尺寸
  3. makepreview:制作平滑的预览图
  4. spheretocube:球形或圆柱形全景图像转换为立方体
  5. cubetosphere:立方体全景图像转换回球形全景图像
  6. protect:生成自定义保护设置的viewer文件
  7. encrypt:加密文件
  8. testserver:测试服务
  9. register:授权码注册

详细文档建议查看官方文档

image.png

我们主要通过makepano命令来切图,这是官方的示例命令krpanotools makepano [-config=###] [OPTIONS] inputfiles …

config参数是切图所依赖的配置文件,默认是当前路径下的templates/vtour-multires.config文件,OPTIONS参数用于设置或覆盖配置文件的设定,inputfiles参数是全景图片的路径,可以是一个或者多个图片

在cmd中执行这个命令切图

image.png

切图完成后会在图片所在的文件夹下生成一个vtour文件夹

image.png

然后点击测试服务,发现可以正常预览

image.png

2.2配置文件的修改

切图后生成的vtour文件夹里有很多东西我们都是不需要的,我们只需要保存着切好图的panos文件夹和tour.xml,所以要根据需求来修改配置文件,打开vtour-multires.config这个文件,将对应的配置设为false或者注释掉,具体的配置说明可以查看官方文档

image.png

修改完后再执行一次切图,不再生成vtour文件夹了而是生成了切好图的文件夹和xml文件

image.png

image.png

2.3服务端实现切图

知道如何使用krpanotools.exe切图后,我们将krpanotools.exe和对应的配置文件放入项目中

image.png

然后开始设计/upload/panos这个接口,nodejs构建的服务端中我们可以使用child_process模块中的exec方法来执行命令

先将图片下载到本地,然后通过exec执行方法执行切图命令,切图完后通过xml-js这个库拿到生成的xml文件中的分辨率参数multires,之后将切好图的文件夹上传到oss,上传成功之后往数据库添加一条记录,并将结果返回给客户端,最后将本地生成的图片,文件夹和xml文件等都删除掉

/**
* 批量上传
* @param {*} localFolderPath // 文件路径
* @param {*} ossFolderPath // oss路径
*/
async uploadFolder(localFolderPath, ossFolderPath) {
    const files = fs.readdirSync(localFolderPath)
    for (const file of files) {
        const filePath = path.posix.join(localFolderPath, file)
        const ossPath = path.posix.join(ossFolderPath, file)
        if (fs.statSync(filePath).isDirectory()) {
            // 如果是文件夹,递归上传
            await this.uploadFolder(filePath, ossPath)
        } else {
            // 如果是文件,上传到 OSS
            try {
                await this.client.put(ossPath, filePath)
            } catch (error) {
                console.error('Failed to upload file ' + ossPath + ':', error)
            }
        }
    }
}


/**
 * 切图并上传到阿里云oss
 * @param {*} ctx
 * @param {*} next
 */
uploadPano = async (ctx, next) => {
    const files = ctx.request.files
    const { id } = ctx.user
    if (!files.length) return ctx.app.emit('error', { code: 400, message: '文件上传错误' }, ctx)
    try {
        // krpanotools.exe切图
        const execPromise = new Promise((resolve, reject) => {
            exec(
                'krpanotools makepano [-config=templates/vtour-multires.config] ' + files[0].path,
                (error, stdout, stderr) => {
                    if (error) {
                        reject('执行命令时发生错误:' + error)
                    } else {
                        resolve()
                    }
                }
            )
        })
        await execPromise
        // 全景参数
        const view_uuid = files[0].filename.split('.')[0]
        const name = files[0].originalname.split('.')[0]
        const xml_path = files[0].path.split('.')[0] + '.xml'
        const xmlData = fs.readFileSync(xml_path, {
            encoding: 'utf-8',
        })
        const xmltree = JSON.parse(xmljs.xml2json(xmlData, { compact: true, spaces: 4 }))
        const multires = xmltree.krpano.scene.image.cube._attributes.multires
        // 切图上传到oss
        await this.uploadFolder('uploads/' + view_uuid, '/panos/' + id + '/' + view_uuid)
        // 预览和文件夹的路径
        const path = '/panos/' + id + '/' + view_uuid
        const thumbUrl = '/panos/' + id + '/' + view_uuid + '/thumb.jpg'
        const data = {
            scene_id: view_uuid,
            path,
            thumbUrl,
            name,
            multires,
            albumid: id,
        }

        await fileService.addPanoMaterials(data)

        ctx.body = {
            code: 200,
            message: '上传成功',
            data,
        }
        // 删除本地文件夹,图片和xml文件
        fs.rmSync('uploads/' + view_uuid, { recursive: true })
        fs.unlinkSync(files[0].path)
        fs.unlinkSync(xml_path)
    } catch (error) {
        console.log(error.message)
        const message = error.sqlMessage ? error.sqlMessage : error.message ? error.message : 'error'
        return ctx.app.emit('error', { code: 500, message }, ctx)
    }
}

a.gif

结尾

以上就是我对服务端实现切图的实现,如果还有更好的方式也欢迎大家一起分享交流,学习学习😊🤞💋😘

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值