课程名称:Vue Element+Node.js开发企业通用管理后台系统(第12章)
课程章节: 第12章 电子书解析功能开发
主讲老师:Sam
课程内容:
今天学习的内容包括:
- 电子书解析功能开发
课程收获:
- 电子书解析功能开发
电子书解析使用了 epub 库,源码:https://github.com/julien-c/epub
,可以通过 npm install epub
进行安装,或直接将 epub.js
拷贝到 /utils/epub.js
。
epub 基本用法:
import EPub from 'epub'
const epub = new EPub(pathToFile, imageWebRoot, chapterWebRoot)
其中,参数说明如下:
- pathToFile 是 EPUB 文件的文件路径
- imageWebRoot 是图像 URL 的前缀
- chapterWebRoot 是章节 URL 的前缀
使用 epub 库解析电子书
parse() {
return new Promise((resolve, reject) => {
const bookPath = `${UPLOAD_PATH}${this.path}`
if (!this.path || !fs.existsSync(bookPath)) {
reject(new Error('电子书路径不存在'))
}
const epub = new Epub(bookPath)
epub.on('error', err => {
reject(err)
})
epub.on('end', err => {
if (err) {
reject(err)
} else {
let {
title,
language,
creator,
creatorFileAs,
publisher,
cover
} = epub.metadata
// title = ''
if (!title) {
reject(new Error('图书标题为空'))
} else {
this.title = title
this.language = language || 'en'
this.author = creator || creatorFileAs || 'unknown'
this.publisher = publisher || 'unknown'
this.rootFile = epub.rootFile
const handleGetImage = (error, imgBuffer, mimeType) => {
if (error) {
reject(error)
} else {
const suffix = mimeType.split('/')[1]
const coverPath = `${UPLOAD_PATH}/img/${this.fileName}.${suffix}`
const coverUrl = `${UPLOAD_URL}/img/${this.fileName}.${suffix}`
fs.writeFileSync(coverPath, imgBuffer, 'binary')
this.coverPath = `/img/${this.fileName}.${suffix}`
this.cover = coverUrl
resolve(this)
}
}
try {
this.unzip() // 解压电子书
this.parseContents(epub)
.then(({ chapters, chapterTree }) => {
this.contents = chapters
this.contentsTree = chapterTree
epub.getImage(cover, handleGetImage) // 获取封面图片
})
.catch(err => reject(err)) // 解析目录
} catch (e) {
reject(e)
}
}
}
})
epub.parse()
this.epub = epub
})
}
电子书目录解析
parseContents(epub) {
function getNcxFilePath() {
const manifest = epub && epub.manifest
const spine = epub && epub.spine
const ncx = manifest && manifest.ncx
const toc = spine && spine.toc
return (ncx && ncx.href) || (toc && toc.href)
}
/**
* flatten方法,将目录转为一维数组
*
* @param array
* @returns {*[]}
*/
function flatten(array) {
return [].concat(...array.map(item => {
if (item.navPoint && item.navPoint.length) {
return [].concat(item, ...flatten(item.navPoint))
} else if (item.navPoint) {
return [].concat(item, item.navPoint)
} else {
return item
}
}))
}
/**
* 查询当前目录的父级目录及规定层次
*
* @param array
* @param level
* @param pid
*/
function findParent(array, level = 0, pid = '') {
return array.map(item => {
item.level = level
item.pid = pid
if (item.navPoint && item.navPoint.length) {
item.navPoint = findParent(item.navPoint, level + 1, item['$'].id)
} else if (item.navPoint) {
item.navPoint.level = level + 1
item.navPoint.pid = item['$'].id
}
return item
})
}
if (!this.rootFile) {
throw new Error('目录解析失败')
} else {
const fileName = this.fileName
return new Promise((resolve, reject) => {
const ncxFilePath = Book.genPath(`${this.unzipPath}/${getNcxFilePath()}`) // 获取ncx文件路径
const xml = fs.readFileSync(ncxFilePath, 'utf-8') // 读取ncx文件
// 将ncx文件从xml转为json
xml2js(xml, {
explicitArray: false, // 设置为false时,解析结果不会包裹array
ignoreAttrs: false // 解析属性
}, function(err, json) {
if (!err) {
const navMap = json.ncx.navMap // 获取ncx的navMap属性
if (navMap.navPoint) { // 如果navMap属性存在navPoint属性,则说明目录存在
navMap.navPoint = findParent(navMap.navPoint)
const newNavMap = flatten(navMap.navPoint) // 将目录拆分为扁平结构
const chapters = []
epub.flow.forEach((chapter, index) => { // 遍历epub解析出来的目录
// 如果目录大于从ncx解析出来的数量,则直接跳过
if (index + 1 > newNavMap.length) {
return
}
const nav = newNavMap[index] // 根据index找到对应的navMap
chapter.text = `${UPLOAD_URL}/unzip/${fileName}/${chapter.href}` // 生成章节的URL
// console.log(`${JSON.stringify(navMap)}`)
if (nav && nav.navLabel) { // 从ncx文件中解析出目录的标题
chapter.label = nav.navLabel.text || ''
} else {
chapter.label = ''
}
chapter.level = nav.level
chapter.pid = nav.pid
chapter.navId = nav['$'].id
chapter.fileName = fileName
chapter.order = index + 1
chapters.push(chapter)
})
const chapterTree = []
chapters.forEach(c => {
c.children = []
if (c.pid === '') {
chapterTree.push(c)
} else {
const parent = chapters.find(_ => _.navId === c.pid)
parent.children.push(c)
}
}) // 将目录转化为树状结构
resolve({ chapters, chapterTree })
} else {
reject(new Error('目录解析失败,navMap.navPoint error'))
}
} else {
reject(err)
}
})
})
}
}
最后,附上课程截图 ending~
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦