前言
前面的文章主要基本介绍了krpano一些元素的用法,从这篇文章开始我们就进入全景开发的实战教学 ,从全景切图 到全景编辑 最后到整个全景的预览,前端使用react框架+antd组件库+krpano,后端使用koa来实现一个类似720云全景的制作网站
在之前的文章中都是通过MAKE VTOUR Droplet.exe 应用程序来手动切图的,但是对于一个制作网站来说肯定是网站来帮用户实现切图,用户只需要上传全景图片,前端拿到图片再传给后端,通过后端来完成切图,所以步骤是-->
用户选择图片切图-->
客户端拿到图片后调用接口进行上传-->
服务端实现切图-->
切图完成
1.用户选择图片并调用接口上传
上传图片通过Upload组件上传,这里将官方文档的示例直接复制过来
js
<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可执行文件执行命令行来切图,在我们从官网下来的包里就包含了这个文件
cmd执行一下krpanotools
命令,可以看到这个应用程序提供好几个功能:
makepano
:制作基于模版的全景或漫游maketiles
:制作或组合切片图像。可用于转换或重置尺寸makepreview
:制作平滑的预览图spheretocube
:球形或圆柱形全景图像转换为立方体cubetosphere
:立方体全景图像转换回球形全景图像protect
:生成自定义保护设置的viewer文件encrypt
:加密文件testserver
:测试服务register
:授权码注册
详细文档建议查看官方文档
我们主要通过makepano
命令来切图,这是官方的示例命令krpanotools makepano [-config=###] [OPTIONS] inputfiles ...
config 参数是切图所依赖的配置文件,默认是当前路径下的templates/vtour-multires.config 文件,OPTIONS 参数用于设置或覆盖配置文件的设定,inputfiles参数是全景图片的路径,可以是一个或者多个图片
在cmd中执行这个命令切图
切图完成后会在图片所在的文件夹下生成一个vtour文件夹
然后点击测试服务,发现可以正常预览
2.2配置文件的修改
切图后生成的vtour文件夹里有很多东西我们都是不需要的,我们只需要保存着切好图的panos 文件夹和tour.xml ,所以要根据需求来修改配置文件,打开vtour-multires.config 这个文件,将对应的配置设为false或者注释掉,具体的配置说明可以查看官方文档
修改完后再执行一次切图,不再生成vtour文件夹了而是生成了切好图的文件夹和xml文件
2.3服务端实现切图
知道如何使用krpanotools.exe切图后,我们将krpanotools.exe和对应的配置文件放入项目中
然后开始设计/upload/panos
这个接口,nodejs构建的服务端中我们可以使用child_process
模块中的exec
方法来执行命令
先将图片下载到本地,然后通过exec执行方法执行切图命令,切图完后通过xml-js 这个库拿到生成的xml文件中的分辨率参数multires,之后将切好图的文件夹上传到oss,上传成功之后往数据库添加一条记录,并将结果返回给客户端,最后将本地生成的图片,文件夹和xml文件等都删除掉
js
/**
* 批量上传
* @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)
}
}
结尾
以上就是我对服务端实现切图的实现,如果还有更好的方式也欢迎大家一起分享交流,学习学习😊🤞💋😘