欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue?大文件分片上傳(斷點(diǎn)續(xù)傳、并發(fā)上傳、秒傳)

 更新時(shí)間:2022年07月08日 08:54:32   作者:小小猴沖刺  
本文主要介紹了vue?大文件分片上傳,主要包括斷點(diǎn)續(xù)傳、并發(fā)上傳、秒傳,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

對(duì)于大文件的處理,無(wú)論是用戶(hù)端還是服務(wù)端,如果一次性進(jìn)行讀取發(fā)送、接收都是不可取,很容易導(dǎo)致內(nèi)存問(wèn)題。所以對(duì)于大文件上傳,采用切塊分段上傳,從上傳的效率來(lái)看,利用多線(xiàn)程并發(fā)上傳能夠達(dá)到最大效率。

本文是基于 springboot + vue 實(shí)現(xiàn)的文件上傳,本文主要介紹vue實(shí)現(xiàn)文件上傳的步驟及代碼實(shí)現(xiàn),服務(wù)端(springboot)的實(shí)現(xiàn)步驟及實(shí)現(xiàn)請(qǐng)移步本人的另一篇文章:

springboot 大文件上傳、分片上傳、斷點(diǎn)續(xù)傳、秒傳 

上傳分步:

本人分析上傳總共分為:

  • MD5讀取文件,獲取文件的MD5編碼
  • 請(qǐng)求服務(wù)端判斷文件是否上傳,如上傳完成就直接返回文件地址
  • 如未上傳,判斷是否是斷點(diǎn)續(xù)傳
  • 判斷是并發(fā)上傳還是順序上傳
  • 開(kāi)始分片文件上傳,分片上傳完成后寫(xiě)入已上傳列表中
  • 判斷是否上傳完成

直接上代碼

文件上傳:

import md5 from 'js-md5' //引入MD5加密
import UpApi from '@/api/common.js'
import { concurrentExecution } from '@/utils/jnxh'
?
/**
?* 文件分片上傳
?* @params file {File} 文件
?* @params pieceSize {Number} 分片大小 默認(rèn)3MB
?* @params concurrent {Number} 并發(fā)數(shù)量 默認(rèn)2
?* @params process {Function} 進(jìn)度回調(diào)函數(shù)
?* @params success {Function} 成功回調(diào)函數(shù)
?* @params error {Function} 失敗回調(diào)函數(shù)
?*/
export const uploadByPieces = ({
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?file,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?pieceSize = 3,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?concurrent = 3,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?success,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?process,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?error
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}) => {
? // 如果文件傳入為空直接 return 返回
? if (!file || file.length < 1) {
? ? return error('文件不能為空')
? }
? let fileMD5 = '' // 總文件列表
? const chunkSize = pieceSize * 1024 * 1024 // 1MB一片
? const chunkCount = Math.ceil(file.size / chunkSize) // 總片數(shù)
? const chunkList = [] // 分片列表
? let uploaded = [] // 已經(jīng)上傳的
? let fileType = '' // 文件類(lèi)型
? // 獲取md5
? /***
? ?* 獲取md5
? ?**/
? const readFileMD5 = () => {
? ? // 讀取視頻文件的md5
? ? fileType = file.name.substring(file.name.lastIndexOf('.') + 1, file.name.length)
? ? console.log('獲取文件的MD5值')
? ? let fileRederInstance = new FileReader()
? ? console.log('file', file)
? ? fileRederInstance.readAsBinaryString(file)
? ? fileRederInstance.addEventListener('load', e => {
? ? ? let fileBolb = e.target.result
? ? ? fileMD5 = md5(fileBolb)
? ? ? var index = file.name.lastIndexOf('.')
? ? ? var tp = file.name.substring(index + 1, file.name.length)
? ? ? let form = new FormData()
? ? ? form.append('filename', file.name)
? ? ? form.append('identifier', fileMD5)
? ? ? form.append('objectType', fileType)
? ? ? form.append('chunkNumber', 1)
? ? ? UpApi.uploadChunk(form).then(res => {
? ? ? ? if (res.skipUpload) {
? ? ? ? ? console.log('文件已被上傳')
? ? ? ? ? success && success(res)
? ? ? ? } else {
? ? ? ? ? // 判斷是否是斷點(diǎn)續(xù)傳
? ? ? ? ? if (res.uploaded && res.uploaded.length != 0) {
? ? ? ? ? ? uploaded = [].concat(res.uploaded)
? ? ? ? ? }
? ? ? ? ? console.log('已上傳的分片:' + uploaded)
? ? ? ? ? // 判斷是并發(fā)上傳或順序上傳
? ? ? ? ? if (concurrent == 1 || chunkCount == 1) {
? ? ? ? ? ? console.log('順序上傳')
? ? ? ? ? ? sequentialUplode(0)
? ? ? ? ? } else {
? ? ? ? ? ? console.log('并發(fā)上傳')
? ? ? ? ? ? concurrentUpload()
? ? ? ? ? }
? ? ? ? }
? ? ? }).catch((e) => {
? ? ? ? console.log('文件合并錯(cuò)誤')
? ? ? ? console.log(e)
? ? ? })
? ? })
? }
? /***
? ?* 獲取每一個(gè)分片的詳情
? ?**/
? const getChunkInfo = (file, currentChunk, chunkSize) => {
? ? let start = currentChunk * chunkSize
? ? let end = Math.min(file.size, start + chunkSize)
? ? let chunk = file.slice(start, end)
? ? return {
? ? ? start,
? ? ? end,
? ? ? chunk
? ? }
? }
? /***
? ?* 針對(duì)每個(gè)文件進(jìn)行chunk處理
? ?**/
? const readChunkMD5 = () => {
? ? // 針對(duì)單個(gè)文件進(jìn)行chunk上傳
? ? for (var i = 0; i < chunkCount; i++) {
? ? ? const {
? ? ? ? chunk
? ? ? } = getChunkInfo(file, i, chunkSize)
?
? ? ? // 判斷已經(jīng)上傳的分片中是否包含當(dāng)前分片
? ? ? if (uploaded.indexOf(i + '') == -1) {
? ? ? ? uploadChunk({
? ? ? ? ? chunk,
? ? ? ? ? currentChunk: i,
? ? ? ? ? chunkCount
? ? ? ? })
? ? ? }
? ? }
? }
? /***
? ?* 原始上傳
? ?**/
? const uploadChunk = (chunkInfo) => {
? ? var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100)
? ? console.log(sd, '進(jìn)度')
? ? process(sd)
? ? console.log(chunkInfo, '分片大小')
? ? let inde = chunkInfo.currentChunk + 1
? ? if (uploaded.indexOf(inde + '') > -1) {
? ? ? const {
? ? ? ? chunk
? ? ? } = getChunkInfo(file, chunkInfo.currentChunk + 1, chunkSize)
? ? ? uploadChunk({
? ? ? ? chunk,
? ? ? ? currentChunk: inde,
? ? ? ? chunkCount
? ? ? })
? ? } else {
? ? ? var index = file.name.lastIndexOf('.')
? ? ? var tp = file.name.substring(index + 1, file.name.length)
? ? ? // 構(gòu)建上傳文件的formData
? ? ? let fetchForm = new FormData()
? ? ? fetchForm.append('identifier', fileMD5)
? ? ? fetchForm.append('chunkNumber', chunkInfo.currentChunk + 1)
? ? ? fetchForm.append('chunkSize', chunkSize)
? ? ? fetchForm.append('currentChunkSize', chunkInfo.chunk.size)
? ? ? const chunkfile = new File([chunkInfo.chunk], file.name)
? ? ? fetchForm.append('file', chunkfile)
? ? ? // fetchForm.append('file', chunkInfo.chunk)
? ? ? fetchForm.append('filename', file.name)
? ? ? fetchForm.append('relativePath', file.name)
? ? ? fetchForm.append('totalChunks', chunkInfo.chunkCount)
? ? ? fetchForm.append('totalSize', file.size)
? ? ? fetchForm.append('objectType', tp)
? ? ? // 執(zhí)行分片上傳
? ? ? let config = {
? ? ? ? headers: {
? ? ? ? ? 'Content-Type': 'application/json',
? ? ? ? ? 'Accept': '*/*'
? ? ? ? }
? ? ? }
?
? ? ? UpApi.uploadChunk(fetchForm, config).then(res => {
?
? ? ? ? if (res.code == 200) {
? ? ? ? ? console.log('分片上傳成功')
? ? ? ? ? uploaded.push(chunkInfo.currentChunk + 1)
? ? ? ? ? // 判斷是否全部上傳完
? ? ? ? ? if (uploaded.length == chunkInfo.chunkCount) {
? ? ? ? ? ? console.log('全部完成')
? ? ? ? ? ? success(res)
? ? ? ? ? ? process(100)
? ? ? ? ? } else {
? ? ? ? ? ? const {
? ? ? ? ? ? ? chunk
? ? ? ? ? ? } = getChunkInfo(file, chunkInfo.currentChunk + 1, chunkSize)
? ? ? ? ? ? uploadChunk({
? ? ? ? ? ? ? chunk,
? ? ? ? ? ? ? currentChunk: chunkInfo.currentChunk + 1,
? ? ? ? ? ? ? chunkCount
? ? ? ? ? ? })
? ? ? ? ? }
?
? ? ? ? } else {
? ? ? ? ? console.log(res.msg)
? ? ? ? }
?
? ? ? }).catch((e) => {
? ? ? ? error && error(e)
? ? ? })
? ? ? // if (chunkInfo.currentChunk < chunkInfo.chunkCount) {
? ? ? // ? setTimeout(() => {
? ? ? //
? ? ? // ? }, 1000)
? ? ? // }
? ? }
? }
? /***
? ?* 順序上傳
? ?**/
? const sequentialUplode = (currentChunk) => {
? ? const {
? ? ? chunk
? ? } = getChunkInfo(file, currentChunk, chunkSize)
? ? let chunkInfo = {
? ? ? chunk,
? ? ? currentChunk,
? ? ? chunkCount
? ? }
? ? var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100)
? ? process(sd)
? ? console.log('當(dāng)前上傳分片:' + currentChunk)
? ? let inde = chunkInfo.currentChunk + 1
? ? if (uploaded.indexOf(inde + '') > -1) {
? ? ? console.log('分片【' + currentChunk + '】已上傳')
? ? ? sequentialUplode(currentChunk + 1)
? ? } else {
? ? ? let uploadData = createUploadData(chunkInfo)
? ? ? let config = {
? ? ? ? headers: {
? ? ? ? ? 'Content-Type': 'application/json',
? ? ? ? ? 'Accept': '*/*'
? ? ? ? }
? ? ? }
? ? ? // 執(zhí)行分片上傳
? ? ? UpApi.uploadChunk(uploadData, config).then(res => {
? ? ? ? if (res.code == 200) {
? ? ? ? ? console.log('分片【' + currentChunk + '】上傳成功')
? ? ? ? ? uploaded.push(chunkInfo.currentChunk + 1)
? ? ? ? ? // 判斷是否全部上傳完
? ? ? ? ? if (uploaded.length == chunkInfo.chunkCount) {
? ? ? ? ? ? console.log('全部完成')
? ? ? ? ? ? success(res)
? ? ? ? ? ? process(100)
? ? ? ? ? } else {
? ? ? ? ? ? sequentialUplode(currentChunk + 1)
? ? ? ? ? }
?
? ? ? ? } else {
? ? ? ? ? console.log(res.msg)
? ? ? ? }
?
? ? ? }).catch((e) => {
? ? ? ? error && error(e)
? ? ? })
? ? }
? }
? /***
? ?* 并發(fā)上傳
? ?**/
? const concurrentUpload = () => {
? ? for (var i = 0; i < chunkCount; i++) {
? ? ? chunkList.push(Number(i))
? ? }
? ? console.log('需要上傳的分片列表:' + chunkList)
? ? concurrentExecution(chunkList, concurrent, (curItem) => {
? ? ? return new Promise((resolve, reject) => {
? ? ? ? const {
? ? ? ? ? chunk
? ? ? ? } = getChunkInfo(file, curItem, chunkSize)
? ? ? ? let chunkInfo = {
? ? ? ? ? chunk,
? ? ? ? ? currentChunk: curItem,
? ? ? ? ? chunkCount
? ? ? ? }
? ? ? ? var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100)
? ? ? ? process(sd)
? ? ? ? console.log('當(dāng)前上傳分片:' + curItem)
? ? ? ? let inde = chunkInfo.currentChunk + 1
? ? ? ? if (uploaded.indexOf(inde + '') == -1) {
? ? ? ? ? // 構(gòu)建上傳文件的formData
? ? ? ? ? let uploadData = createUploadData(chunkInfo)
? ? ? ? ? // 請(qǐng)求頭
? ? ? ? ? let config = {
? ? ? ? ? ? headers: {
? ? ? ? ? ? ? 'Content-Type': 'application/json',
? ? ? ? ? ? ? 'Accept': '*/*'
? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? ? UpApi.uploadChunk(uploadData, config).then(res => {
? ? ? ? ? ? if (res.code == 200) {
? ? ? ? ? ? ? uploaded.push(chunkInfo.currentChunk + 1)
? ? ? ? ? ? ? console.log('已經(jīng)上傳完成的分片:' + uploaded)
? ? ? ? ? ? ? // 判斷是否全部上傳完
? ? ? ? ? ? ? if (uploaded.length == chunkInfo.chunkCount) {
? ? ? ? ? ? ? ? success(res)
? ? ? ? ? ? ? ? process(100)
? ? ? ? ? ? ? }
? ? ? ? ? ? ? resolve()
? ? ? ? ? ? } else {
? ? ? ? ? ? ? reject(res)
? ? ? ? ? ? ? console.log(res.msg)
? ? ? ? ? ? }
?
? ? ? ? ? }).catch((e) => {
? ? ? ? ? ? reject(res)
? ? ? ? ? ? error && error(e)
? ? ? ? ? })
? ? ? ? } else {
? ? ? ? ? console.log('分片【' + chunkInfo.currentChunk + '】已上傳')
? ? ? ? ? resolve()
? ? ? ? }
? ? ? })
? ? }).then(res => {
? ? ? console.log('finish', res)
? ? })
? }
? /***
? ?* 創(chuàng)建文件上傳參數(shù)
? ?**/
? const createUploadData = (chunkInfo) => {
? ? let fetchForm = new FormData()
? ? fetchForm.append('identifier', fileMD5)
? ? fetchForm.append('chunkNumber', chunkInfo.currentChunk + 1)
? ? fetchForm.append('chunkSize', chunkSize)
? ? fetchForm.append('currentChunkSize', chunkInfo.chunk.size)
? ? const chunkfile = new File([chunkInfo.chunk], file.name)
? ? fetchForm.append('file', chunkfile)
? ? // fetchForm.append('file', chunkInfo.chunk)
? ? fetchForm.append('filename', file.name)
? ? fetchForm.append('relativePath', file.name)
? ? fetchForm.append('totalChunks', chunkInfo.chunkCount)
? ? fetchForm.append('totalSize', file.size)
? ? fetchForm.append('objectType', fileType)
? ? return fetchForm
? }
? readFileMD5() // 開(kāi)始執(zhí)行代碼
}

并發(fā)控制:

/**
 * 并發(fā)執(zhí)行
 * @params list {Array} - 要迭代的數(shù)組
 * @params limit {Number} - 并發(fā)數(shù)量控制數(shù),最好小于3
 * @params asyncHandle {Function} - 對(duì)`list`的每一個(gè)項(xiàng)的處理函數(shù),參數(shù)為當(dāng)前處理項(xiàng),必須 return 一個(gè)Promise來(lái)確定是否繼續(xù)進(jìn)行迭代
 * @return {Promise} - 返回一個(gè) Promise 值來(lái)確認(rèn)所有數(shù)據(jù)是否迭代完成
 */
export function concurrentExecution(list, limit, asyncHandle) {
  // 遞歸執(zhí)行
  let recursion = (arr) => {
    // 執(zhí)行方法 arr.shift() 取出并移除第一個(gè)數(shù)據(jù)
    return asyncHandle(arr.shift()).then(() => {
      // 數(shù)組還未迭代完,遞歸繼續(xù)進(jìn)行迭代
      if (arr.length !== 0) {
        return recursion(arr)
      } else {
        return 'finish'
      }
    })
  }
  // 創(chuàng)建新的并發(fā)數(shù)組
  let listCopy = [].concat(list)
  // 正在進(jìn)行的所有并發(fā)異步操作
  let asyncList = []
  limit = limit > listCopy.length ? listCopy.length : limit
  console.log(limit)
  while (limit--) {
    asyncList.push(recursion(listCopy))
  }
  // 所有并發(fā)異步操作都完成后,本次并發(fā)控制迭代完成
  return Promise.all(asyncList)
}

到此這篇關(guān)于vue 大文件分片上傳(斷點(diǎn)續(xù)傳、并發(fā)上傳、秒傳)的文章就介紹到這了,更多相關(guān)vue 大文件分片上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vuex + keep-alive實(shí)現(xiàn)tab標(biāo)簽頁(yè)面緩存功能

    vuex + keep-alive實(shí)現(xiàn)tab標(biāo)簽頁(yè)面緩存功能

    這篇文章主要介紹了vuex + keep-alive實(shí)現(xiàn)tab標(biāo)簽頁(yè)面緩存功能,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-10-10
  • vue使用keep-alive后清除緩存的方法

    vue使用keep-alive后清除緩存的方法

    這篇文章主要給大家介紹了關(guān)于vue使用keep-alive后清除緩存的相關(guān)資料,這個(gè)問(wèn)題在我們?nèi)粘9ぷ髦薪?jīng)常會(huì)用到,本文通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • Vue項(xiàng)目開(kāi)發(fā)常見(jiàn)問(wèn)題和解決方案總結(jié)

    Vue項(xiàng)目開(kāi)發(fā)常見(jiàn)問(wèn)題和解決方案總結(jié)

    這篇文章主要介紹了Vue項(xiàng)目開(kāi)發(fā)常見(jiàn)問(wèn)題和解決方案總結(jié),幫助大家更好的利用vue開(kāi)發(fā),感興趣的朋友可以了解下
    2020-09-09
  • Vue組件之間的通信方式詳細(xì)講解

    Vue組件之間的通信方式詳細(xì)講解

    對(duì)于vue來(lái)說(shuō),組件之間的消息傳遞是非常重要的,用vue可以是要組件復(fù)用的,而組件實(shí)例的作用域是相互獨(dú)立,這意味著不同組件之間的數(shù)據(jù)無(wú)法互相引用,一般來(lái)說(shuō),組件之間可以有幾種關(guān)系,下面是我對(duì)組件之間消息傳遞的常用方式的總結(jié)
    2022-08-08
  • 不同場(chǎng)景下Vue中虛擬列表實(shí)現(xiàn)

    不同場(chǎng)景下Vue中虛擬列表實(shí)現(xiàn)

    虛擬列表用來(lái)解決大數(shù)據(jù)量數(shù)據(jù)渲染問(wèn)題,由于一次性渲染性能低,所以誕生了虛擬列表渲染,下面我們就來(lái)學(xué)習(xí)一下不同場(chǎng)景下Vue中虛擬列表是如何實(shí)現(xiàn)的吧
    2023-10-10
  • 說(shuō)說(shuō)Vuex的getters屬性的具體用法

    說(shuō)說(shuō)Vuex的getters屬性的具體用法

    這篇文章主要介紹了說(shuō)說(shuō)Vuex的getters屬性的具體用法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-04-04
  • 在vue中如何封裝G2圖表

    在vue中如何封裝G2圖表

    這篇文章主要介紹了在vue中如何封裝G2圖表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue-video-player 斷點(diǎn)續(xù)播的實(shí)現(xiàn)

    vue-video-player 斷點(diǎn)續(xù)播的實(shí)現(xiàn)

    這篇文章主要介紹了vue-video-player 斷點(diǎn)續(xù)播的實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • 基于vue實(shí)現(xiàn)圓形菜單欄組件

    基于vue實(shí)現(xiàn)圓形菜單欄組件

    這篇文章主要介紹了基于vue實(shí)現(xiàn)的圓形菜單欄組件,本文通過(guò)實(shí)例代碼,圖文詳解的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-07-07
  • 基于Vue.js實(shí)現(xiàn)簡(jiǎn)單搜索框

    基于Vue.js實(shí)現(xiàn)簡(jiǎn)單搜索框

    這篇文章主要為大家詳細(xì)介紹了基于Vue.js實(shí)現(xiàn)簡(jiǎn)單搜索框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11

最新評(píng)論