• 前端上传数据-按解析顺序


    前言

    后后需要支持excel上传内容,格式如下:

    由于我司多媒体文件在七牛保存,若后端上传数据,则要先保存到后端服务器,存储消耗不容忽视;而且上传数据可能会失败,然后涉及记录失败条目、失败重传、当前进度等
    开发周期较长,因此小组讨论后决定采用比较简单的策略:

    1. 按 excel 行顺序上传,提示当前正在上传的行号
    2. 单选上传多媒体资源完成后,将内容保存到数据库
    3. 记录失败的行,上传结束后给出提示,用户自行重传失败的条目

    然而理想总是丰满的,现实总是骨感的,实现时才发现不是看上去那么容易。
    主要问题:

    1. 我们要等单个 row 里的资源全部上传到七牛后,再将数据 post 到后台,而 js 异步执行,无法保证按excel行顺序上传
    2. 用户选择的资源可能并不是真正的多媒体文件,使用 js 的 file.type 方法获取的文件类型可能不准确,比如将 .xlsx 改为 .jpg, file.type 得到的类型是image/jpeg
    3. 用户在表里填写了文件名,实际上传时可能漏传一些文件,这时候即便成功,也是错误的数据

    所谓兵来将挡水来土掩,对以上问题,摸索出解决方案如下:

    1. 使用 Promise 模型,多层嵌套
    2. 根据文件后缀和二进制头,双重判断文件类型
    3. 用户选择文件后即进行校验,缺少文件则无法上传

    Promise 模型

    js 的Promise支持链式调用,因此单个 row 的资源文件,调用 Promise.all() 全部上传到七牛后,再将该行内容发送给后端,然后进行下一步。

    发送数据给后端

    例如发送数据给后端可以这样实现

    // 声明一个返回 promise 的函数
    function sendToDB(data){
      return new Promise((resolve, reject) => {
        createArticleApi(data)
        .then(resp=>{
          resolve()
        })
        .catch(err => {
          reject()
        })
      })
    }
    // 执行
    arr.reduce(
      (promise, data) => {
        return promise.then(() => {
          sendToDB(data)
        })
      }, Promise.resolve()
    )
    .then(data => {})
    .catch(err => {})
    

    然后上传的时候调用链

    promise.resolve(row1).then(row2).then(row3).then(row4)...
    

    但实际上传时,由于Promise链上的任一reject会触发catch异常,而发送给后端可能返回失败,导致excel未全部上传完毕就提前退出,因此每个Promise要单独执行,出错后能控制继续执行 or 终止

    这里要用到js 语法 (function f(){})() 声明并立即调用函数,函数执行完后返回的promise决定是否继续

    let idx = 1
    // 直接调用第一个promise, 启动
    let p = new Promise(resolve => {
      resolve(sendToDB(arr[0]))
    })
    // 若写入数据库出错,返回resolve继续执行
    while(idx <= arr.length){
      (function(idx){
        p = p.then(() => {
          sendToDb(arr[idx])
          .then(()=>{})
          .catch(()=>{})
          if(idx === arr.length){
            return
          }
          return arr[idx]
        }).catch(() => {
          if(idx === arr.length){
            return
          }
          // 忽略错误,继续执行
          return Promise.resolve()
        })
      })(idx)
      idx += 1
    }
    

    上传的时候执行类似

    promise.resolve(row1.then(resolve))
    .then(row2.catch(resolve))
    .then(row3.catch(resolve))
    .then(row4.then(resolve))...
    

    上传文件到七牛

    而前面封装好的七牛接口,返回的也是promise对象,而我要在调用时才拿返回结果,所以需要一个返回七牛上传结果的函数

    js 的闭包可实现这个功能,调用函数后返回一个返回promise的函数

    闭包

    // 七牛上传接口为异步
    // promise 组确保每一行的资源上传完成后,才开始下一步执行
    function promiseFunc(line){
      return function(){
        return new Promise((resolve, reject) => {
          // 当前上传的行号
          _this.uploadingLineNo = line.lineno
          let imgsPromises = line.imgs.map(name => {
              return _this.uploadSingleFile(name, 'image')
          })
    
          let mediasPromises = line.medias.map(name => {
              return _this.uploadSingleFile(name, 'video')
          })
    
          Promise.all([Promise.all(imgsPromises), Promise.all(mediasPromises)])
          .then(data=>{ 
            line.imgUrls = data[0]
            line.mediaUrls = data[1]
            resolve(line)
          })
          .catch(err => {
            // 行号和对应的上传结果
            _this.resourceFileResult.push({
              lineno: line.lineno,
              error: err || '包含不允许的文件类型'
            })
            line.imgUrls = []
            line.mediaUrls = []
            reject()
          })
        })
      }
    } 
    

    然后将excel内容映射成上传函数,推入数组,接下来使用下标调用函数

    核心代码

    // promise 队列,按 excel 顺序上传内容
    let asyncArr = _this.fileContentList.map(promiseFunc)
    let idx = 1
    // 直接调用第一个promise, 启动
    let p = new Promise(resolve => {
      resolve(asyncArr[0]())
    })
    // 若上传七牛或 写入数据库出错,返回resolve继续执行
    while(idx <= asyncArr.length){
      (function(idx){
        p = p.then(data => {
          _this.sendToDB(data)
          .then(()=>{})
          .catch(()=>{})
          if(idx === asyncArr.length){
            return
          }
          return asyncArr[idx]()
        }).catch(() => {
          if(idx === asyncArr.length){
            return
          }
          // 忽略上传七牛错误,继续执行
          return Promise.resolve()
        })
      })(idx)
      idx += 1
    }
    
  • 相关阅读:
    将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组
    vue 设计一个倒计时秒杀的组件
    es6之三个点(...)扩展运算符
    es6之解构赋值
    es6之字符串添加的东西
    ES6系列之变量声明let const
    js对数组进行删除
    python学习
    泛型
    随笔
  • 原文地址:https://www.cnblogs.com/wbjxxzx/p/10341777.html
Copyright © 2020-2023  润新知