课程名称: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)
其中,参数说明如下:
使用 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~