最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue實(shí)現(xiàn)封裝一個切片上傳組件

 更新時(shí)間:2023年03月16日 11:30:06   作者:前端小小夢  
平時(shí)業(yè)務(wù)開發(fā)中用el-upload能滿足大部分場景,但是對于一些大文件的上傳時(shí)會比較慢,所以自己基于el-upload封裝了一個切片上傳組件,希望對大家有所幫助

組件效果

單文件切片上傳

多文件切片上傳

組件使用案例

<template>
  <div id="app">
    <div class="upload-wrap">
      <UploadSlice
        :action="uploadInfoSlice.actionChunk"
        :headers="uploadInfoSlice.headers"
        :limit="uploadInfoSlice.limit"
        :accept="uploadInfoSlice.accept"
        :show-file-list="false"
        cancelable
        :on-success="handleSuccess"
        :on-remove="handleRemove"
        :on-cancel="handleCancel"
        :on-upload-pre="handleUploadPre"
        :on-upload-merge="handleUploadMerge"
        :on-form-data="genFormData"
      />
    </div>
  </div>
</template>

<script>
import UploadSlice from './components/UploadSlice.vue'
import { uploadPre, uploadMerge } from '@/api/upload-slice'

export default {
  name: 'App',
  components: {
    UploadSlice
  },
  data() {
    return {
      // 上傳部分
      uploadInfoSlice: {
        actionChunk: process.env.VUE_APP_BASE_API + '/storage/file/v3/chunk', // 切片請求上傳路徑
        headers: { 'Authorization': 'Bearer XXX' }
      }
    }
  },
  methods: {
    // 分片預(yù)請求
    async handleUploadPre(file) {
      const form = new FormData()
      form.append('fileSource', 'APPLICATION')
      form.append('originFileName', file.name)
      let res = ''
      try {
        res = await uploadPre(form)
      } catch (error) {
        throw new Error(error)
      }
    },
    // 構(gòu)造分片參數(shù)
    genFormData(chunks, uid) {
      const prepareId = this.getCurrentPrepareId(uid)
      return chunks.map(chunk => {
        const form = new FormData()
        form.append('chunk', chunk.file)
        form.append('uploadId', prepareId)
        form.append('partNumber', chunk.index)
        return form
      })
    },
    // 合并請求
    async handleUploadMerge(file, uid) {
      const prepareId = this.getCurrentPrepareId(uid)
      const form = new FormData()
      form.append('fileSource', 'APPLICATION')
      form.append('hash', prepareId)
      form.append('filename', file.name)
      // return 建議使用, 用于handleSuccess獲取數(shù)據(jù)
      try {
        const res = await uploadMerge(form)
        return res
      } catch (error) {
        return error
      }
    },
    // 判斷當(dāng)前處理prepareId
    getCurrentPrepareId(uid) {
      for (const item of this.progressFileList) {
        if (item.uid === uid) {
          return item.prepareId
        }
      }
    }
  }
}
</script>

使用文檔

Attribute

標(biāo)紅色部分為二次封裝處理過的功能,其他為el-upload自帶屬性

參數(shù)說明類型可選值默認(rèn)值備注
action必選參數(shù),分片上傳的地址,預(yù)請求和合并請求在組件外操作String--
headers設(shè)置上傳的請求頭部String--
multiple是否支持多選文件boolean-
accept可上傳文件類型,多種類型用","分隔 (格式不符合自動提示)String--
on-remove文件列表移除文件時(shí)的鉤子function(file, fileList)
on-success文件上傳成功時(shí)的鉤子function(response, file, fileList)
on-error文件上傳失敗時(shí)的鉤子function(err, file, fileList)
on-progress文件上傳時(shí)的鉤子function(event, file, fileList)
on-change文件狀態(tài)改變時(shí)的鉤子,添加文件、上傳成功和上傳失敗時(shí)都會被調(diào)用function(file, fileList)
on-exceed文件超出個數(shù)限制時(shí)的鉤子function(files, fileList)
list-type文件列表的類型stringtext/picture/picture-cardtext
show-file-list是否顯示已上傳文件列表(文件分片上傳時(shí)建議設(shè)置false,否則會有兩個進(jìn)度條)booleantrue
file-list上傳的文件列表, 例如: [{name: 'food.jpg', url: 'xxx.cdn.com/xxx.jpg'}]array[]
disabled是否禁用booleanfalse
cancelable是否支持取消booleanfalse
limit最多允許上傳個數(shù)(超出個數(shù)自動提示)number
size限制大小String
hideBtn是否在上傳過程中隱藏上傳按鈕booleanfalse

Slot

插槽名說明
trigger觸發(fā)文件選擇框的內(nèi)容
tip提示說明文字
more-tips在默認(rèn)提示后補(bǔ)充說明

封裝過程

切片上傳組件是基于el-upload進(jìn)行的二次封裝,文章開頭組件效果演示可以看到上傳一個文件會發(fā)送三個請求:prepare,chunk, merge,也就是整個上傳過程,主要分為三步:1.預(yù)請求 2.分片請求 3.合并請求,預(yù)請求和合并請求就是我們正常的http請求,主要處理的是分片請求,分片請求主要的步驟是:

  • 將文件切片
  • 構(gòu)造切片請求參數(shù)
  • 控制分片請求的并發(fā)

1. 文件切片

el-upload上傳后, 在on-change屬性的回調(diào)里可以獲取文件file,通過file.raw.slice對文件進(jìn)行切片,目前的切片規(guī)則是:1.小于10M 固定一片 2.小于50M 文件10%為一片 3.大于50M 固定5M 一片(可以根據(jù)自己的需求進(jìn)行修改)

genFileChunks(file) {
  const chunks = []
  let cur = 0
  // 小于10M 固定一片
  if (file.size < (10 * 1024 * 1024)) {
    chunks.push({
      index: cur,
      file: file.raw.slice(cur, file.size),
      originFilename: file.name
    })
    return chunks
  }
  // 小于50M 文件10%為一片
  if (file.size < (50 * 1024 * 1024)) {
    const chunkSize = parseInt(file.size * 0.1)
    while (cur < file.size) {
      chunks.push({
        index: cur,
        file: file.raw.slice(cur, cur + chunkSize),
        originFilename: file.name
      })
      cur += chunkSize
    }
    return chunks
  }
  // 大于50M 固定5M 一片
  const chunkSize = parseInt(5 * 1024 * 1024)
  while (cur < file.size) {
    chunks.push({
      index: cur,
      file: file.raw.slice(cur, cur + chunkSize),
      originFilename: file.name
    })
    cur += chunkSize
  }
  return chunks
},

一個32M的文件按照10%切一片,構(gòu)造好的切片數(shù)據(jù)是這樣的

2. 構(gòu)造切片請求參數(shù)

切片請求不同業(yè)務(wù)的參數(shù)是變化的,所以參數(shù)部分可以拋出給父組件處理,增加組件的復(fù)用性

父組件

<template>
  <UploadSlice
    :action="uploadInfoSlice.actionChunk"
    :headers="uploadInfoSlice.headers"
    :on-form-data="genFormData"
  />
</template>

<script>
  methods: {
    // 構(gòu)造分片參數(shù)
    genFormData(chunks, uid) {
      const prepareId = this.getCurrentPrepareId(uid)
      return chunks.map(chunk => {
        const form = new FormData()
        form.append('chunk', chunk.file)
        form.append('uploadId', prepareId)
        form.append('partNumber', chunk.index)
        return form
      })
    },
  },
</script>

子組件

<template>
  <el-upload
      action=""
      :accept="accept"
  >
</template>

<script>
  props: {
    onFormData: {
      type: Function,
      default: () => {}
    },
  },
  methods: {
    async uploadChunks(uid) {
      // 預(yù)請求
      // ---------------

      // 上傳切片
      const requests = this._genRequest(this._genUploadData(uid), uid)
      // 控制并發(fā)
      await this.sendRequest(requests)
      
      // 合并請求
      // ---------------
    },
    
    // 構(gòu)造分片參數(shù)
    _genUploadData(uid) {
      const chunks = this.getCurrentChunks(uid)
      return this.onFormData(chunks, uid)
    },
    
    // 生成調(diào)用請求:[Promise, Promise]
    _genRequest(uploadData, uid) {
      console.log('uploadData', uploadData)
      const file = this.getCurrentFile(uid)
      const chunks = this.getCurrentChunks(uid)
      return uploadData.map((form, index) => {
        const options = {
          headers: this.$attrs.headers,
          file: file,
          data: form,
          action: this.action,
          onProgress: progress => {
            chunks[index].progress = Number(
              ((progress.loaded / progress.total) * 100).toFixed(2)
            )
            this.handleProgress(progress, file, uid)
          }
        }
        return options
      })
    },
  },
</script>

3. 控制分片請求的并發(fā)

切片上傳如果不控制并發(fā),在分片很多時(shí),就會同時(shí)發(fā)送很多個http請求,導(dǎo)致線程阻塞,影響頁面其他請求的操作,所以控制并發(fā)是需要的。我設(shè)置的是最多允許3個并發(fā)請求。

    sendRequest(requests, limit = 3) {
      return new Promise((resolve, reject) => {
        const len = requests.length
        let counter = 0
        let isTips = false // 只提示一次失敗
        let isStop = false // 如果一個片段失敗超過三次 認(rèn)為當(dāng)前網(wǎng)洛有問題 停止全部上傳
        const startRequest = async() => {
          if (isStop) return
          const task = requests.shift()
          if (task && task.file.status !== 'cancel') {
            // 利用try...catch捕獲錯誤
            try {
              // 具體的接口  抽離出去了
              await ajax(task)
              if (counter === len - 1) { // 最后一個任務(wù)
                resolve()
              } else { // 否則接著執(zhí)行
                counter++
                startRequest() // 啟動下一個任務(wù)
              }
            } catch (error) {
              // 網(wǎng)絡(luò)異常
              if (error === 'NETWORK_ERROR' && !isTips) {
                Message.error('網(wǎng)絡(luò)異常,文件上傳失敗')
                this.upLoading = false
                this.preLoading = false
                isTips = true
                this.handleRemove('', [])
              }

              // 接口報(bào)錯重試,限制為3次
              if (task.error < 3) {
                task.error++
                requests.unshift(task)
                startRequest()
              } else {
                isStop = true
                reject(error)
              }
            }
          }
        }
        // 啟動任務(wù)
        while (limit > 0) {
          // 模擬不同大小啟動
          setTimeout(() => {
            startRequest()
          }, Math.random() * 2000)
          limit--
        }
      })
    }
  }

完整代碼

文章只整理了核心代碼,對于格式數(shù)量限制、進(jìn)度條處理、取消上傳等操作也進(jìn)行了封裝,詳細(xì)請看完整代碼

https://github.com/Xmengling/el-upload-slice

待完善

  • 目前還沒有做斷點(diǎn)續(xù)傳的處理
  • 可能還有一些異常邊界情況沒有考慮完整

到此這篇關(guān)于Vue實(shí)現(xiàn)封裝一個切片上傳組件的文章就介紹到這了,更多相關(guān)Vue封裝切片上傳組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue.js中npm安裝教程圖解

    vue.js中npm安裝教程圖解

    這篇文章主要介紹了vue.js中npm安裝教程圖解,非常不錯,具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-04-04
  • vuejs實(shí)現(xiàn)標(biāo)簽選項(xiàng)卡動態(tài)更改css樣式的方法

    vuejs實(shí)現(xiàn)標(biāo)簽選項(xiàng)卡動態(tài)更改css樣式的方法

    這篇文章主要介紹了vuejs實(shí)現(xiàn)標(biāo)簽選項(xiàng)卡-動態(tài)更改css樣式的方法,代碼分為html和js兩部分,需要的朋友可以參考下
    2018-05-05
  • vue?Element?UI擴(kuò)展內(nèi)容過長使用tooltip顯示

    vue?Element?UI擴(kuò)展內(nèi)容過長使用tooltip顯示

    這篇文章主要為大家介紹了vue?Element?UI擴(kuò)展內(nèi)容過長使用tooltip展示鼠標(biāo)hover時(shí)的提示信息,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 詳解vue-router基本使用

    詳解vue-router基本使用

    本篇文章主要介紹了詳解vue-router基本使用,詳細(xì)的介紹了vue-router的概念和用法,有興趣的可以了解一下。
    2017-04-04
  • vue實(shí)現(xiàn)菜單切換功能

    vue實(shí)現(xiàn)菜單切換功能

    這篇文章主要介紹了vue實(shí)現(xiàn)菜單切換功能,代碼簡單易懂,非常不錯,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-05-05
  • 全面解析vue中的數(shù)據(jù)雙向綁定

    全面解析vue中的數(shù)據(jù)雙向綁定

    這篇文章主要介紹了vue中的數(shù)據(jù)雙向綁定問題,非常不錯,具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-05-05
  • vue開發(fā)中遇到的問題總結(jié)

    vue開發(fā)中遇到的問題總結(jié)

    在本篇文章里小編給大家分享的是關(guān)于vue開發(fā)中遇到的問題總結(jié),有興趣的朋友們可以學(xué)習(xí)參考下。
    2020-04-04
  • vue.set向?qū)ο罄镌黾佣鄬訑?shù)組屬性不生效問題及解決

    vue.set向?qū)ο罄镌黾佣鄬訑?shù)組屬性不生效問題及解決

    這篇文章主要介紹了vue.set向?qū)ο罄镌黾佣鄬訑?shù)組屬性不生效問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue3+axios封裝攔截器方式

    vue3+axios封裝攔截器方式

    介紹了如何在Vue項(xiàng)目中使用Axios封裝請求、配置攔截器,并在api.js中統(tǒng)一管理API接口,同時(shí),也講解了如何在vite.config.js中配置解決跨域問題,這些操作可以優(yōu)化前端代碼結(jié)構(gòu),提高開發(fā)效率
    2024-09-09
  • 關(guān)于Vue中的watch監(jiān)視屬性

    關(guān)于Vue中的watch監(jiān)視屬性

    這篇文章主要介紹了關(guān)于Vue中的watch監(jiān)視屬性,Vue中的watch默認(rèn)不監(jiān)視對象內(nèi)部值的改變,當(dāng)被監(jiān)視的屬性變化時(shí),回調(diào)函數(shù)自動調(diào)用,進(jìn)行相關(guān)操作,需要的朋友可以參考下
    2023-04-04

最新評論

拉孜县| 甘泉县| 通榆县| 长兴县| 洪泽县| 沙田区| 方城县| 息烽县| 汶上县| 乌鲁木齐市| 赤峰市| 汨罗市| 裕民县| 读书| 兰坪| 兴国县| 东兴市| 彭泽县| 铁岭县| 桐梓县| 新蔡县| 石景山区| 谢通门县| 砀山县| 普定县| 平定县| 龙海市| 玛多县| 新巴尔虎左旗| 绍兴县| 上饶县| 喀喇| 额尔古纳市| 谢通门县| 南皮县| 宿迁市| 滨海县| 仪陇县| 堆龙德庆县| 晋州市| 崇左市|