电子书解析

电子书解析

思路:上传电子书的时候使用了multer中间件来完成上传过程,上传之后会在req产生一个file对象,这个file对象表示一个数组来代表文件的序列,file对象下包含了文件对象,文件对象里包含了文件名,文件路径,文件资源类型等等,那到这些信息之后就可以通过这些信息生成一个book对象,这里的book对象就是所说的电子书对象,然后通过book对象来完成解析的过程

models/Book.js

这里的book就代表一本电子书,他必须要给我们提供一些能力,这些能力包含从文件当中去创建对象,还有一种是在编辑的时候,需要能够根据表单的数据把它也变成一个book对象

变成book对象以后有什么好处呢?通过解析成book对象以后,就可以写一些方法比如parse方法对book对象进行解析,就可以解析到里面的一些细节信息,比如language,title ,creator等,

可以解析出电子书的目录,同时能将book对象转换成json格式(可以直接拿给前端使用),可以转换成数据库字段名快速的生成一些sql语句,所以book对象对应我们来开发整个电子书解析部分是至关重要的,所以,电子书解析很大一部分都是在编写book对象。

电子书book对象开发

传入file表示刚上传了一个电子书的文件,如果传入一个data表示更新或者插入电子书数据,data表示向数据库中插入数据,file主要用于解析电子书数据

在这里插入图片描述

在这里插入图片描述

router/book.js调用

在这里插入图片描述

在这里插入图片描述

知道file对象的内容了以后,就可以对他进行解析了

mimetype可以给他一个默认类型

在这里插入图片描述

我们需要给文件改个名字,因为发现返回的file.path路径是没有后缀名的,去识别这个文件的时候会有些麻烦(suffix)

在这里插入图片描述

生成文件的下载路径 定义url constant.js

这里改了一下UPLOAD_PATH 不用两个反斜杠了 一个‘/’也可以

这里发现了一个上古时期的bug UPLOAD_PATH后面不应该有\book的

在这里插入图片描述

    // 生成文件下载路径 通过这个下载路径就可以快速的下载到电子书
    const url=`${
      
      UPLOAD_URL}/book/${
      
      filename}${
      
      suffix}`

解压后的文件夹同理

const {
    
    MIME_TYPE_EPUB,UPLOAD_URL,UPLOAD_PATH}=require('../utils/constant')
class Book{
    
    
  constructor(file,data){
    
    
    if(file){
    
    
      this.createBookFromFile(file)
    }else{
    
    
      this.createBookFromData(data)
    }
  }
  createBookFromFile(file){
    
    
    // console.log("createBookFromFile",file);
    const{
    
    
      destination,
      filename,
      mimetype=MIME_TYPE_EPUB,
      path
    }=file
    // 电子书的文件后缀名
    const suffix=mimetype===MIME_TYPE_EPUB?'.epub':''
    // 电子书原有路径
    const oldBookPath=path    // 原有路径
    // 电子书新路径
    const bookPath=`${
      
      destination}\\${
      
      filename}${
      
      suffix}` //新路径 
    // 生成文件下载路径 通过这个下载路径就可以快速的下载到电子书
    // 电子书的下载URL
    const url=`${
      
      UPLOAD_URL}/book/${
      
      filename}${
      
      suffix}`
    // 生成电子书解压文件夹 文件夹以文件名命名
    // 电子书解压后的文件夹路径
    const unzipPath=`${
      
      UPLOAD_PATH}\\unzip\\${
      
      filename}`
    // 这个url路径会在电子书阅读的时候使用到它
    // 电子书解压后的文件夹URL
    const unzipUrl=`${
      
      UPLOAD_URL}/unzip/${
      
      filename}`

  }
  createBookFromData(){
    
    

  }
}
module.exports=Book

接下来可以创建一下电子书的解压文件夹

  if(!fs.existsSync(unzipPath)){
    
    
      // 不存在的话迭代创建文件夹
      fs.mkdirSync(unzipPath,{
    
    recursive:true})
    }

在这里插入图片描述

接下来解压以后的文件就会丢到这个路径下面

对文件进行重命名

 // 判断当前电子书是否存在 如果存在且新的电子书不存在的情况下 
 // 调用rename对文件夹重命名的方法,把oldBookPath和bookPath传入实现重命名 
if(fs.existsSync(oldBookPath)){
    
    
      fs.renameSync(oldBookPath,bookPath)
    }

在这里插入图片描述

接下来根据前端所需要的一些字段定义book对象的一些属性

 this.filename=filename  // 无后缀的文件名
    // 写相对路径,为了兼容不同的场景 因为在服务端和客户端他的绝对路径是不一样
    this.path=`/book/${
      
      filename}${
      
      suffix}` // epub文件相对路径
    this.filePath=this.path    // 起一个别名
    this.unzipPath=`/unzip/${
      
      filename}`  // 解压后相对路径
    this.url=url     // epub文件下载链接
    this.title=''   // 标题或书名,解析后生成 
    this.author=''  
    this.publisher=''    // 出版社
    this.contents=[]    // 目录
    this.cover=''     // 封面图片url
    this.category=-1    // 分类id
    this.categoryText=''  // 分类名称
    this.language=''    // 语种
    this.unzipUrl=unzipUrl   // 解压后的文件夹链接
    this.originalname=originalname  // 原始名

看一下结果(这里两个反斜杠的都应该改成/)

在这里插入图片描述

电子书解析库epub库

epubjs库是用于浏览器场景,脱离浏览器是无法工作的,因为他主要是在浏览器场景下对浏览器进行渲染

这里的epub库是在node环境下进行使用的

https://www.youbaobao.xyz/admin-docs/guide/extra/book.html#%E7%94%B5%E5%AD%90%E4%B9%A6%E8%A7%A3%E6%9E%90-2

https://github.com/julien-c/epub/blob/master/epub.js

因为需要对他的代码进行修改,所以拷贝一下集成到项目中,不是通过npm包安装的方式

utils/epub.js

在这里插入图片描述

安装adm-zip xml2js

Epub类提供了一个parse方法

在这里插入图片描述

实际去解析的时候就用到了parse方法

看一下使用方法 是使用event来实现的

在这里插入图片描述

传入之后,就用了一个回调的方法

在这里插入图片描述

后面的function是解析成功之后的回调

什么时候开始手动解析呢 需要调用epub.parse()

通过epub实例调用getChapter方法,里面再去调用一个回调

作者,标题这些信息可以去metadata里面获取

解析成功之后可以通过epub.metadata拿到

在这里插入图片描述

flow是整个电子书渲染的次序

在这里插入图片描述

getChapter获取章节(传入章节id) 获取章节对应的文本

在这里插入图片描述

getChapterRaw表示获得的原始文本,也就是一个html格式的文件

getImage 传入图片id 拿到图片实际的内容

getFile 传入css的id,拿到css的文件

在这里插入图片描述

因为存在大量的回调情况,后面会对他进行改造

电子书解析方法上

model/Book.js引入epub库

新增一个parse方法 我们给Book增加了很多属性,但是有很多都是默认值,在parse里解析,之后再给填充上

  parse(){
    
    
    return new Promise((resolve,reject)=>{
    
    
      const bookPath=`${
      
      UPLOAD_PATH}${
      
      this.filePath}1`
      // 如果不存在文件路径 抛出错误
      if(!fs.existsSync(bookPath)){
    
    
        reject (new Error('电子书不存在'))
      } 
    })
  }

测试一下

router/book.js

在这里插入图片描述

为了验证,把路径随便改一下

在这里插入图片描述

在这里插入图片描述

前端一直卡在这里 是因为没有返回内容,可以用到boom来快速生成异常对象

.catch(err => {
    
    
        console.log("upload", err);
        // 告诉前端发生了解析异常
        next(boom.badImplementation(err))
      })

在这里插入图片描述

就是说在Book对象中使用reject包装的error 会通过路由返回给next 然后被自定义异常捕获再返回给前端,前端再进行相应的处理,这样服务端抛出的异常就可以被前端捕获到了

在这里插入图片描述

电子书解析方法下

在这里插入图片描述

看到调用parse方法之后 他会调用一个open方法

在这里插入图片描述

在这里插入图片描述

在model/Book.js:

消费

在这里插入图片描述

reject后

在这里插入图片描述

接着会到自定义异常处理

在这里插入图片描述

测试

在这里插入图片描述

在这里插入图片描述


把bookPath改回来

打印出来epub.metadata

在这里插入图片描述

需要解析出来metadata里的信息


打印book

在这里插入图片描述


epub对象:

containerFile :epub解析的第一个文件 根据这个文件找content.opf

rootFile:就是content.opf的位置,因为阅读电子书的时候其实就是要解析content.opf

只要能找到content.opf 那么后面的流程就好办了

manifest:资源文件 通过资源文件就可以找到封面图片

toc:目录

在这里插入图片描述

book对象打印

在这里插入图片描述

获取封面:通过epub库提供的getImage方法

在这里插入图片描述

这个方法需要传入两个参数,一个是id一个是callback

id就是封面图片对应的id

在这里插入图片描述

我们就是要把href里的图片拷贝到nginx目录下的img文件夹下 这样的话我们就可以拿到url链接,拿到这个链接就可以作为封面图片的链接

分析一下getImage方法的源码

    getImage(id, callback) {
    
    
      // 到manifest找链接
        if (this.manifest[id]) {
    
    
              // 判断media-type 如果存在会把前面的六个字符截取与image/对比,不相等的话抛出异常 在callback中传入一个error对象
            if ((this.manifest[id]['media-type'] || "").toLowerCase().trim().substr(0, 6)  !=  "image/") {
    
    
                return callback(new Error("Invalid mime type for image"));
            }
            // 如果是图片的话就调用getFile  把id和callback传入
            this.getFile(id, callback);
        } else {
    
    
            callback(new Error("File not found"));
        }
    };

看一下getFile如何实现的

在这里插入图片描述

来使用一下getImage方法 其实可以对库做一些翻新,用.then或者async await更好, 这里先用回调

在这里插入图片描述

在这里插入图片描述

这样data数据已经获取到了 (读取到了内存当中 还不在磁盘当中)

在这里插入图片描述

suffix根据mimetype来获取

写入文件

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

打卡Book.js代码

const {
    
     MIME_TYPE_EPUB, UPLOAD_URL, UPLOAD_PATH } = require('../utils/constant')
const fs = require('fs')
const Epub = require('../utils/epub')
class Book {
    
    
  constructor(file, data) {
    
    
    if (file) {
    
    
      this.createBookFromFile(file)
    } else {
    
    
      this.createBookFromData(data)
    }
  }
  createBookFromFile(file) {
    
    
    console.log("createBookFromFile", file);
    const {
    
    
      destination,
      filename,
      mimetype = MIME_TYPE_EPUB,
      path,
      originalname
    } = file
    // 电子书的文件后缀名
    const suffix = mimetype === MIME_TYPE_EPUB ? '.epub' : ''
    // 电子书原有路径
    const oldBookPath = path    // 原有路径
    // 电子书新路径
    const bookPath = `${
      
      destination}/${
      
      filename}${
      
      suffix}` //新路径 
    // 生成文件下载路径 通过这个下载路径就可以快速的下载到电子书
    // 电子书的下载URL
    const url = `${
      
      UPLOAD_URL}/book/${
      
      filename}${
      
      suffix}`
    // 生成电子书解压文件夹 文件夹以文件名命名
    // 电子书解压后的文件夹路径
    const unzipPath = `${
      
      UPLOAD_PATH}/unzip/${
      
      filename}`
    // 这个url路径会在电子书阅读的时候使用到它
    // 电子书解压后的文件夹URL
    const unzipUrl = `${
      
      UPLOAD_URL}/unzip/${
      
      filename}`
    // 如果不存在unzipPath,就去创建
    if (!fs.existsSync(unzipPath)) {
    
    
      // 不存在的话迭代创建文件夹
      fs.mkdirSync(unzipPath, {
    
     recursive: true })
    }
    // 判断当前电子书是否存在 如果存在且新的电子书不存在的情况下 
    // 调用rename对文件夹重命名的方法,把oldBookPath和bookPath传入实现重命名
    if (fs.existsSync(oldBookPath)) {
    
    
      fs.renameSync(oldBookPath, bookPath)
    }
    this.filename = filename  // 无后缀的文件名
    // 写相对路径,为了兼容不同的场景 因为在服务端和客户端他的绝对路径是不一样
    this.path = `/book/${
      
      filename}${
      
      suffix}` // epub文件相对路径
    this.filePath = this.path    // 起一个别名  电子书相对路径
    this.unzipPath = `/unzip/${
      
      filename}`  // 解压后相对路径
    this.url = url     // epub文件下载链接
    this.title = ''   // 标题或书名,解析后生成 
    this.author = ''
    this.publisher = ''    // 出版社
    this.contents = []    // 目录
    this.cover = ''     // 封面图片url
    this.coverPath=''
    this.category = -1    // 分类id
    this.categoryText = ''  // 分类名称
    this.language = ''    // 语种
    this.unzipUrl = unzipUrl   // 解压后的文件夹链接
    this.originalname = originalname  // 原始名

  }
  createBookFromData() {
    
    
  }
  parse() {
    
    
    return new Promise((resolve, reject) => {
    
    
      const bookPath = `${
      
      UPLOAD_PATH}${
      
      this.filePath}`
      // 如果不存在文件路径 抛出错误
      if (!fs.existsSync(bookPath)) {
    
    
        reject(new Error('电子书不存在'))
      }
      // 创建个实例
      const epub = new Epub(bookPath)
      // error回调,判断解析过程中有没有出现异常
      epub.on('error', err => {
    
    
        reject(err)
      })
      // end事件表示电子书成功解析
      epub.on('end', err => {
    
    
        if (err) {
    
    
          reject(err)
        } else {
    
    
          // console.log("epub+ ", epub.manifest);
          const {
    
    
            language,
            creator,
            creatorFileAs,
            title,
            cover,
            publisher
          } = epub.metadata

          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 =  (err, file, mimetype) =>{
    
    
              console.log(err, file, mimetype);
              if (err) {
    
    
                reject(err)
              } else {
    
    
                // 需要整个电子书解析完之后再调用resolve,而不是直接调完getImage就resolve,
                //因为getImage可能出错,调完resolve再调reject逻辑就会出现问题
                const suffix = mimetype.split('/')[1]
                const coverPath = `${
      
      UPLOAD_PATH}/img/${
      
      this.filename}.${
      
      suffix}`
                const coverUrl = `${
      
      UPLOAD_URL}/img/${
      
      this.filename}.${
      
      suffix}`
                // 把buffer写入到磁盘当中
                console.log(coverPath);
                fs.writeFileSync(coverPath,file,'binary')
                this.coverPath=`/img/${
      
      this.filename}.${
      
      suffix}`
                this.cover=coverUrl
                resolve(this)
              }
            }
            epub.getImage(cover, handleGetImage)
             // resolve(this)  不要在这里写
          }
        }
      })
      epub.parse()
    })
  }
}
module.exports = Book

封面图片解析优化

有的电子书用这种方法是获取不到封面图片的

在这里插入图片描述

看一下这个错误出现在哪里

在这里插入图片描述

打印一下cover

在这里插入图片描述

解压出来分析一下

打开package.opf

metadata里没有标签是关于cover的 说明没有办法获得封面图片的资源id了

在manifest里找找

在这里插入图片描述

能看到封面的资源文件,但是是xhtml的类型,说明他是章节的内容,并不是图片 图片应该是image开头的

这个才是封面图片

在这里插入图片描述

他是在image路径下978开头的文件

在这里插入图片描述

所以封面还有一种查询方式 就是读取item下面的properties 如果为cover-image 表示是封面图片 可以把这个图片的href获取到,然后来找到它的资源文件并且从epub当中解压出来保存到本地

需要对epub.js的getImage方法改造

如果manifest没有办法从cover下获取的时候需要改进一下这里的逻辑

大致框架

在这里插入图片描述

如何获取coverId在这里插入图片描述

   const coverId=Object.keys(this.manifest).find(key=>{
    
      //注意这里不是花括号
      
        // console.log(key,this.manifest[key]);
        this.manifest[key].properties==='cover-image'
      })
getImage(id, callback) {
    
    
    // 到manifest找链接
    if (this.manifest[id]) {
    
    
      // 判断media-type 如果存在会把前面的六个字符截取与image/对比,不相等的话抛出异常 在callback中传入一个error对象
      if ((this.manifest[id]['media-type'] || "").toLowerCase().trim().substr(0, 6) != "image/") {
    
    
        return callback(new Error("Invalid mime type for image"));
      }
      // 如果是图片的话就调用getFile  把id和callback传入
      this.getFile(id, callback);
    } else {
    
    
      // 传入的id无法用  需要获取到coverId 判断coverId是否存在
      // 这样就把符合条件的key返回
      const coverId = Object.keys(this.manifest).find(key => (
        // console.log(key,this.manifest[key]);
        this.manifest[key].properties === 'cover-image'
      ))
      console.log("coverId", coverId);
      if (coverId) {
    
    
        this.getFile(coverId, callback)
      } else {
    
    
        callback(new Error("File not found"));
      }

    }
  };


接下来开发一个比较有难度的点–解析电子书目录

在epub库并没有提供解决方案,manifest目录虽然有很多的资源文件,但是并没有形成一个顺序,我们还要确定目录的层级关系

目录解析原理和电子书解压

目录解析原理

先从spin标签下面获取toc属性 (目录的资源id)

在这里插入图片描述

之后在manifest里找

在这里插入图片描述

打开toc.ncx

在这里插入图片描述

navMap:导航

里面都是目录,目录可能会出现嵌套的情况

1.对电子书文件进行解压

解压后放unzip的文件夹下

通过之前getFile方法,能够直接获取到电子书文件,但是我们选择先对他进行解压,这样读取的效率会更高一些

来到自己编写的Book类中,

在这里插入图片描述

编写unzip方法

 unzip(){
    
    
    const AdmZip=require('adm-zip')
    const zip=new AdmZip(Book.genPath(this.path))
    // zip对象的api extractAllTo()  含义是将路径下的文件进行解压,
    // 解压以后把它放到一个新的路径下  第二个参数:是否进行覆盖
    zip.extractAllTo(Book.genPath(this.unzipPath),true)
  }
  // 生成静态方法,获得绝对路径
  static genPath(path){
    
    
    if(!path.startsWith('/')){
    
    
      path=`/${
      
      path}`
    }
    return `${
      
      UPLOAD_PATH}${
      
      path}`
  }
}

在这里插入图片描述

解压出来以后就可以对他进行解析了

unzip方法是个同步方法

unzip过后就可以定义一个parseContents了 传入epub对象 因为要去toc spin里面去找toc属性

  parseContents(epub){
    
    
    function getNcxFilePath(){
    
    
      const spine=epub&&epub.spine
      console.log("spine",spine);
    }
    getNcxFilePath()
  }

打印出spine

在这里插入图片描述

可以看到spine下面有个toc属性

可以找到toc对应的id/直接拿href也可以

如果没有href的时候,就找id->找manifest

parseContents(epub){
    
    
    function getNcxFilePath(){
    
    
      const spine=epub&&epub.spine
      const manifest=epub&&epub.manifest
      const ncx=spine.toc&&spine.toc.href
      const id=spine.toc&&spine.toc.id
      console.log("spine", spine.toc,ncx,id,manifest[id].href);
      if(ncx){
    
    
        return ncx
      }else{
    
    
        // 这个一定会存在的,因为这是电子书的目录
        return manifest[id].href
      }
    }
    getNcxFilePath()
  }

在这里插入图片描述

可以发现两种方法都可以获取到目录

之后来拿到路径

 const ncxFilePath=getNcxFilePath()

这样拿的是相对路径 需要把它拼成绝对路径

 const ncxFilePath=Book.genPath(getNcxFilePath())

这样还是不对,需要加上unzipPath

 const ncxFilePath=Book.genPath(`${
      
      this.unzipPath}/${
      
      getNcxFilePath()}`)
    console.log(ncxFilePath);

还要做一件事情,判断这个路径是否存在,如果不存在需要抛出异常

    if(fs.existsSync(ncxFilePath)){
    
    

    }else{
    
    
      throw new Error('目录对应的资源文件不存在')
    }

在这里catch到

在这里插入图片描述

最终前端会拿到错误信息

在这里插入图片描述

试验一下

   const ncxFilePath=Book.genPath(`${
      
      this.unzipPath}/${
      
      getNcxFilePath()+1}`)

在这里插入图片描述

电子书标准目录解析

打开toc.ncx

在这里插入图片描述

在ncx对象下面有个navMap

navMap下面每一个navPoint都是一个目录选项

在这里插入图片描述

navLabel:具体的目录内容

content :src 目录的路径,playOrder:目录顺序

在这里插入图片描述

目录可能存在嵌套,我们还需要对二级目录进行识别,所以需要有一个迭代的方法实现目录的识别(难点)

Book.js首先引用xml2js库

https://www.npmjs.com/package/xml2js

在这里插入图片描述

在这里插入图片描述

我们要取的是ncx下面的navMap属性

打印一下navMap

在这里插入图片描述

技巧 :看一下详细信息

在这里插入图片描述

字符串粘到json.cn里面

在这里插入图片描述

在这里插入图片描述

目录结构

在这里插入图片描述

返回的结果他给包裹在一个数组中了,如果不希望包裹在数组中,可以加个参数,

在这里插入图片描述

在这里插入图片描述

       xml2js(xml,{
    
    
          explicitArray:false,
          ignoreAttrs:false
        },function(err,result){
    
    
          if(err){
    
    
            reject(err)
          }else{
    
    
            console.log(result) 
            const navMap=result.ncx.navMap
            console.log(JSON.stringify(navMap));
          }
        })

现在的结构

在这里插入图片描述

增加findParent方法 因为这是单级的目录,所以返回一样的数组,以后会完善

    function findParent(array){
    
    
      return array.map(item=>{
    
    
        return item
      })
    }

如果是有子目录的话,是一个树状结构,树状结构是不利于前端展示的

所以需要把树状结构改成一维的结构 现在还没有这个场景,但是还是先把方法建好

 navMap.navPoint=findParent(avMap.navPoint)
 const newNavMap=flatten(navMap.navPoint)

newNavMap是对navMap做的一个浅拷贝

    function findParent(array){
    
    
      return array.map(item=>{
    
    
        return item
      })
    }
    function flatten(array){
    
    
      return [].concat(...array.map(item=>{
    
    
        return item
      }))
    }

newNavMap是复制了一份数组


epub.flow:展示顺序

在这里插入图片描述

  epub.flow.forEach((chapter,index)=>{
    
    
                if(index+1>newNavMap.length){
    
    
                  // flow里面的信息超过目录信息 就return
                  return 
                }else{
    
    
                  // 没有超过的时候
                  // 拿到目录信息
                  const nav=newNavMap[index]
                  // 增加个属性(章节url)
                  chapter.text=`${
      
      UPLOAD_URL}/unzip/${
      
      fileName}/${
      
      chapter.href}`
                  console.log(chapter.text);
                }
              })
              console.log(epub.flow);
            }else{
    
    
              reject('目录解析失败,目录树为0')
            }

在这里插入图片描述

在这里插入图片描述

有个问题,直接用epub.flow不行吗

其实用epub.flow是有一些隐含的坑的

在这里插入图片描述

有的电子书是没有order和level的,不精确

所以从navMap中获取比较正宗的目录信息

在继续向chapter里面加属性

                  if (nav && nav.navLabel) {
    
    
                    chapter.label = nav.navLabel.text || ''
                  } else {
    
    
                    chapter.label = ''
                  }
                  chapter.navId=nav['$'].id
                  chapter.fileName=fileName
                  chapter.order=index+1
                  chapters.push(chapter)
                  console.log(chapter.text);

在这里插入图片描述

嵌套目录解析

在这里插入图片描述

在findParent里做一些文章

level 默认为0 下一级为1 这样返回给前端的时候就可以根据level做缩进

传入3个参数 array,level=0,pid=0

navPoint里面是没有level字段的,可以给他加个level字段

    function findParent(array, level = 0, pid = '') {
    
    
      // 三个场景,一:没有包含navPoint:直接复值level,pid
      // 二:存在navPoint同时navPoint又是数组 说明下面包含子目录 进行迭代
      // 三:navPoint可能不是数组而是个对象的时候(只有一个目录),直接赋值
      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
      })
    }

在这里插入图片描述

flatten方法:将navPoint数组变成一个扁平状态

配合这里

在这里插入图片描述

如果不变扁平那newNavMap的长度一定小于index+1(flow)

flatten方法

    function flatten(array) {
    
    
      return [].concat(...array.map(item => {
    
    
        // 如果包含的是数组
        if(item.navPoint&&item.navPoint.length>0){
    
    
          // 合并
          return [].concat(item,...flatten(item.navPoint))
        }else if(item.navPoint){
    
    
          // 如果是个对象
          return [].concat(item,item.navPoint)
        }
        return item
      }))
    }

resolve reject

在这里插入图片描述

在这里插入图片描述

可以把book返回给前端

new Result(book,‘上传成功’).success(res)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiaozhazhazhazha/article/details/119412400
今日推荐