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

vue3+element 分片上傳與分片下載功能實(shí)現(xiàn)方法詳解

 更新時(shí)間:2023年06月10日 09:16:26   作者:@七葉一枝花  
這篇文章主要介紹了vue3+element 分片上傳與分片下載功能實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了vue3+element 分片上傳與下載相關(guān)實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下

思路:分片上傳是把一個(gè)大文件切割若干等份,前端循環(huán)調(diào)用上傳接口進(jìn)行上傳。分片下載也是一樣的道理,前端調(diào)用接口拿到文件總大小,計(jì)算分割成多少份,循環(huán)調(diào)用下載接口獲取每一段的文件流,獲取全部文件片段,進(jìn)行合并下載。

一、安裝依賴(lài)

用于獲取文件的唯一標(biāo)識(shí),后端會(huì)根據(jù)此標(biāo)識(shí)判斷是否傳過(guò)這個(gè)文件,傳過(guò)的話就直接返回文件路徑,提示上傳成功

npm install spark-md5

方法封裝

import SparkMD5 from 'spark-md5'
// 獲取文件的唯一MD5標(biāo)識(shí)碼
export function getFileMd5(file) {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader()
    const spark = new SparkMD5.ArrayBuffer()
    fileReader.readAsArrayBuffer(file)
    fileReader.onload = e => {
      spark.append(e.target.result)
      let md5 = spark.end()
      resolve(md5)
    }
  })
}

二、分片上傳

 <el-upload :show-file-list="false" :auto-upload="false" :limit="1" :on-change="handleChange" :on-exceed="handleExceed" :multiple="false">
        <el-button type="primary" :loading="modelObj.loading">{{ modelObj.loading ? '上傳中...' : buttonTitle_ }}</g-button>
      </el-upload>

上傳的邏輯

import { getFileMd5 } from './method'
import { computed, reactive} from 'vue'
const modelObj = reactive({
  fileList: {},
  loading: false,
  percentage: 0,
})
// 上傳之后重新點(diǎn)擊上傳
const handleExceed = uploadFile => {
  modelObj.fileList = {}
  handleChange({ raw: uploadFile[0] })
}
//后端接口
const { upLoadBypiece, downLoadbyPiece } = api
// 文件上傳 選擇文件時(shí)觸發(fā)(:on-change事件)
const handleChange = async (uploadFile, uploadFiles) => {
  modelObj.percentage = 0
  // 文件信息
  let fileRaw = uploadFile.raw
  modelObj.fileName = fileRaw.name
  console.log(fileRaw, 'fileRaw')
  modelObj.loading = true
  // 獲取 文件的 MD5唯一標(biāo)識(shí)碼
  let fileMd5 = null
  try {
    fileMd5 = await getFileMd5(fileRaw)
  } catch (e) {
    console.error('[error]', e)
  }
  if (!fileMd5) return
  // 每片的大小為 5M 可調(diào)整
  const chunkSize = 5 * 1024 * 1024
  // 文件分片儲(chǔ)存
  let chunkList = []
  function chunkPush(page = 1) {
    chunkList.push(fileRaw.slice((page - 1) * chunkSize, page * chunkSize))
    if (page * chunkSize < fileRaw.size) {
      chunkPush(page + 1)
    }
  }
  chunkPush()
  saveFileChunk(chunkList, fileMd5, fileRaw.name)
}
// 保存文件片段到后臺(tái)
const saveFileChunk = async (chunkList, fileMd5, fileName) => {
  for (let i = 0; i < chunkList.length; i++) {
    let formData = new FormData()
    formData.append('filePath', props.filePath) // minio存儲(chǔ)的路徑
    formData.append('chunk', i) // 當(dāng)前片段的索引
    formData.append('chunkSize', 5 * 1024 * 1024) // 切片的文件分片大小 (就是以多少字節(jié)進(jìn)行分片的,這里是5M)
    formData.append('chunks', chunkList.length) // 共有多少分片
    formData.append('chunkFile', chunkList[i]) // 當(dāng)前分片的文件流
    formData.append('md5', fileMd5) // 整個(gè)文件的MD5唯一標(biāo)識(shí)碼,不是分片
    formData.append('fileName', fileName) // 文件的名稱(chēng)
    formData.append('size', chunkList[i].size) // 當(dāng)前切片的大?。ㄗ詈笠黄灰欢ㄊ?M)
    try {
      const data = await upLoadBypiece(formData)
      //計(jì)算當(dāng)前上傳進(jìn)度百分比,展示進(jìn)度條
      modelObj.percentage = Math.floor(((i + 1) / chunkList.length) * 100)
      //成功的時(shí)候接口會(huì)返回文件的相關(guān)信息,當(dāng)有data.fileName,說(shuō)明上傳成功了
      if (data.fileName) {
        modelObj.percentage = 100
        modelObj.loading = false
        modelObj.fileList = data
        emit('getFile', modelObj.fileList)
        console.log(modelObj.fileList, 'modelObj.fileList')
        message.success(`上傳成功`)
        return
      }
    } catch (e) {
      modelObj.loading = false
    }
  }
}

效果圖如下
在這里插入圖片描述

三、分片下載

(1)分片下載合并核心偽代碼

 let fileBlob=[]
 for (let index = 0; index < 5; index++) {
      const params={}
      const config={}
      const data = await downLoadbyPiece(params, config)
      //存儲(chǔ)每一片文件流
      fileBlob.push(data.data)
  }
  //合并
  const blob = new Blob(fileBlob, {
    type:fileBlob[0].type,
  })
  //下載
  const link = document.createElement('a')
  link.href = window.URL.createObjectURL(blob)
  link.download = fileName
  link.click()
  window.URL.revokeObjectURL(link.href)

(2)思路:

1、前端第一次調(diào)用接口,請(qǐng)求頭默認(rèn)傳 Range:bytes=0-chunkSize ,第一段的文件大小。接口會(huì)在響應(yīng)頭返回Content-Range: bytes 0-5242880/534107865,534107865即為總文件大小,需要根據(jù)這個(gè)總文件大小以及每片大小chunkSize ,計(jì)算分為多少段下載。

請(qǐng)求頭

第一次Range: bytes=0-5242880,第二次請(qǐng)求Range: bytes=5242880-10485760,依次遞增
在這里插入圖片描述

響應(yīng)頭

Content-Disposition用于獲取文件名稱(chēng);Content-Range獲取總文件大小

在這里插入圖片描述
2、根據(jù)總文件大小以及每片大小chunkSize計(jì)算合并成數(shù)組uploadRange。格式如下
在這里插入圖片描述
3、第一次調(diào)用已經(jīng)傳了Range: bytes=0-5242880,所以循環(huán)是從數(shù)組第2位開(kāi)始調(diào)用下載接口

(3)代碼

<g-button type="primary" :loading="downloadObj.downloading" @click="download"> {{ downloadObj.downloading ? '下載中...' : '下載文件' }}</g-button>
    <span> 下載進(jìn)度({{ downloadObj.percentage }}%)</span>
//下載邏輯
const downloadObj = reactive({
  fileName: '',
  downloading: false,
  range: 0,
  fileBlob: [],
  percentage: 0,
})
const download = async () => {
  downloadObj.fileBlob = []//存接口返回的每一段的文件流
  downloadObj.downloading = true
  downloadObj.range = 0 //文件總大小
  downloadObj.percentage = 0 //下載進(jìn)度
  const params = {
    md5: '73333a4795dfdfgv266454bbbgfdge41f',
  }
  const chunkSize = 5 * 1024 * 1024
  //第一次調(diào)接口獲取到響應(yīng)頭的content-range,文件總大小,用于計(jì)算下載切割
  const config = {
    headers: {
      Range: `bytes=0-${chunkSize}`,
    },
  }
  const data = await downLoadbyPiece(params, config)
  //獲取文件總大小
  const arr = data.headers['content-range'].split('/')
  downloadObj.range = Number(arr[1])
  //存儲(chǔ)每片文件流
  downloadObj.fileBlob.push(data.data)
  //獲取文件名稱(chēng)
  let fileName = ''
  let cd = data.headers['content-disposition']
  if (cd) {
    let index = cd.lastIndexOf('=')
    fileName = decodeURI(cd.substring(index + 1, cd.length))
  }
  await chunkUpload(params, fileName, chunkSize)
}
//拿到文件總大小downloadObj.range,計(jì)算分為多少都段下載
const chunkUpload = async (params, fileName, chunkSize) => {
  //獲取分段下載的數(shù)組
  let chunkList = []
  function chunkPush(page = 1) {
    chunkList.push((page - 1) * chunkSize)
    if (page * chunkSize < downloadObj.range) {
      chunkPush(page + 1)
    }
  }
  chunkPush()
  chunkList.push(downloadObj.range)
  console.log(chunkList, 'chunkList')
  //分段組合傳參格式處理 0-1024 1024-2048
  let uploadRange = []
  chunkList.forEach((item, i) => {
    if (i == chunkList.length - 1) return
    uploadRange.push(`${chunkList[i]}-${chunkList[i + 1]}`)
  })
  console.log(uploadRange, 'uploadRang')
  for (let index = 0; index < uploadRange.length; index++) {
    if (index > 0) {
      const config = {
        headers: {
          Range: `bytes=${uploadRange[index]}`,
        },
      }
      const data = await downLoadbyPiece(params, config)
      //計(jì)算下載進(jìn)度
      downloadObj.percentage = Math.floor(((index + 1) / uploadRange.length) * 100)
      emit('getDownloadpercent', downloadObj.percentage)
      //存儲(chǔ)每一片文件流
      downloadObj.fileBlob.push(data.data)
    }
  }
  //合并
  const blob = new Blob(downloadObj.fileBlob, {
    type: downloadObj.fileBlob[0].type,
  })
  downloadObj.downloading = false
  //下載
  const link = document.createElement('a')
  link.href = window.URL.createObjectURL(blob)
  link.download = fileName
  link.click()
  window.URL.revokeObjectURL(link.href)
}

四、上傳下載完整代碼

<template>
  <div>
    <div>
      <el-upload :show-file-list="false" :auto-upload="false" :limit="1" :on-change="handleChange" :on-exceed="handleExceed" :multiple="false">
        <g-button type="primary" :loading="modelObj.loading">{{ modelObj.loading ? '上傳中...' : buttonTitle_ }}</g-button>
      </el-upload>
    </div>
    <div v-show="modelObj.fileName && showPercentage">
      <span>{{ modelObj.fileName }}</span>
      <span>{{ modelObj.percentage == 100 ? '上傳完成' : '上傳中' }}({{ modelObj.percentage }}%)</span>
      <!-- 使用進(jìn)度條 -->
      <!-- <el-progress :stroke-width="10" :percentage="modelObj.percentage" /> -->
    </div>
    <g-button type="primary" :loading="downloadObj.downloading" @click="download"> {{ downloadObj.downloading ? '下載中...' : '下載文件' }}</g-button>
    <span> 下載進(jìn)度({{ downloadObj.percentage }}%)</span>
  </div>
</template>
<script setup>
import { computed, reactive, ref } from 'vue'
import { getFileMd5 } from './method'
import { useMessage } from 'ui'
const message = useMessage()
const props = defineProps({
  buttonTitle: {
    type: String,
    default: '上傳文件',
  },
  //minio存儲(chǔ)的路徑
  filePath: {
    type: String,
    default: 'data/data',
  },
  //是否展示進(jìn)度條
  showPercentage: {
    type: Boolean,
    default: true,
  },
})
const buttonTitle_ = computed(() => props.buttonTitle)
const modelObj = reactive({
  fileList: {},
  loading: false,
  percentage: 0,
})
const emit = defineEmits(['getFile', 'getDownloadpercent'])
const { upLoadBypiece, downLoadbyPiece } = api
// 上傳之后重新點(diǎn)擊上傳
const handleExceed = uploadFile => {
  modelObj.fileList = {}
  handleChange({ raw: uploadFile[0] })
}
// 文件上傳 選擇文件時(shí)觸發(fā)(:on-change事件)
const handleChange = async (uploadFile, uploadFiles) => {
  modelObj.percentage = 0
  // 文件信息
  let fileRaw = uploadFile.raw
  modelObj.fileName = fileRaw.name
  modelObj.loading = true
  // 獲取 文件的 MD5唯一標(biāo)識(shí)碼
  let fileMd5 = null
  try {
    fileMd5 = await getFileMd5(fileRaw)
  } catch (e) {
    console.error('[error]', e)
  }
  if (!fileMd5) return
  // 每片的大小為 5M 可調(diào)整
  const chunkSize = 5 * 1024 * 1024
  // 文件分片儲(chǔ)存
  let chunkList = []
  function chunkPush(page = 1) {
    chunkList.push(fileRaw.slice((page - 1) * chunkSize, page * chunkSize))
    if (page * chunkSize < fileRaw.size) {
      chunkPush(page + 1)
    }
  }
  chunkPush()
  saveFileChunk(chunkList, fileMd5, fileRaw.name)
}
// 保存文件片段到后臺(tái)
const saveFileChunk = async (chunkList, fileMd5, fileName) => {
  for (let i = 0; i < chunkList.length; i++) {
    let formData = new FormData()
    formData.append('filePath', props.filePath) // minio存儲(chǔ)的路徑
    formData.append('chunk', i) // 當(dāng)前片段的索引
    formData.append('chunkSize', 5 * 1024 * 1024) // 切片的文件分片大小 (就是以多少字節(jié)進(jìn)行分片的,這里是5M)
    formData.append('chunks', chunkList.length) // 共有多少分片
    formData.append('chunkFile', chunkList[i]) // 當(dāng)前分片的文件流
    formData.append('md5', fileMd5) // 整個(gè)文件的MD5唯一標(biāo)識(shí)碼,不是分片
    formData.append('fileName', fileName) // 文件的名稱(chēng)
    formData.append('size', chunkList[i].size) // 當(dāng)前切片的大小(最后一片不一定是5M)
    try {
      const data = await upLoadBypiece(formData)
      //計(jì)算當(dāng)前上傳進(jìn)度百分比,展示進(jìn)度條
      modelObj.percentage = Math.floor(((i + 1) / chunkList.length) * 100)
      //成功的時(shí)候接口會(huì)返回文件的相關(guān)信息,當(dāng)有data.fileName,說(shuō)明上傳成功了
      if (data.fileName) {
        modelObj.percentage = 100
        modelObj.loading = false
        modelObj.fileList = data
        emit('getFile', modelObj.fileList)
        console.log(modelObj.fileList, 'modelObj.fileList')
        message.success(`上傳成功`)
        return
      }
    } catch (e) {
      modelObj.loading = false
    }
  }
}
//下載邏輯
const downloadObj = reactive({
  fileName: '',
  downloading: false,
  range: 0,
  fileBlob: [],
  percentage: 0,
})
const download = async () => {
  downloadObj.fileBlob = []
  downloadObj.downloading = true
  downloadObj.range = 0 //文件總大小
  downloadObj.percentage = 0 //下載進(jìn)度
  const params = {
    md5: '7343784583fsdufhusdfgsudfe8934',
  }
  const chunkSize = 5 * 1024 * 1024
  //第一次調(diào)接口獲取到響應(yīng)頭的content-range,文件總大小,用于計(jì)算下載切割
  const config = {
    headers: {
      Range: `bytes=0-${chunkSize}`,
    },
  }
  const data = await downLoadbyPiece(params, config)
  //獲取文件總大小
  const arr = data.headers['content-range'].split('/')
  downloadObj.range = Number(arr[1])
  //存儲(chǔ)每片文件流
  downloadObj.fileBlob.push(data.data)
  //獲取文件名稱(chēng)
  let fileName = ''
  let cd = data.headers['content-disposition']
  if (cd) {
    let index = cd.lastIndexOf('=')
    fileName = decodeURI(cd.substring(index + 1, cd.length))
  }
  await chunkUpload(params, fileName, chunkSize)
}
//拿到文件總大小downloadObj.range,計(jì)算分為多少都段下載
const chunkUpload = async (params, fileName, chunkSize) => {
  //獲取分段下載的數(shù)組
  let chunkList = []
  function chunkPush(page = 1) {
    chunkList.push((page - 1) * chunkSize)
    if (page * chunkSize < downloadObj.range) {
      chunkPush(page + 1)
    }
  }
  chunkPush()
  //加上文件大小在最后一位
  chunkList.push(downloadObj.range)
  console.log(chunkList, 'chunkList')
  //分段組合傳參格式處理 0-1024 1024-2048
  let uploadRange = []
  chunkList.forEach((item, i) => {
    if (i == chunkList.length - 1) return
    uploadRange.push(`${chunkList[i]}-${chunkList[i + 1]}`)
  })
  console.log(uploadRange, 'uploadRang')
  for (let index = 0; index < uploadRange.length; index++) {
    //第一次調(diào)接口已經(jīng)傳過(guò)了第一組,從第二位開(kāi)始
    if (index > 0) {
      const config = {
        headers: {
          Range: `bytes=${uploadRange[index]}`,
        },
      }
      const data = await downLoadbyPiece(params, config)
      //計(jì)算下載進(jìn)度
      downloadObj.percentage = Math.floor(((index + 1) / uploadRange.length) * 100)
      emit('getDownloadpercent', downloadObj.percentage)
      //存儲(chǔ)每一片文件流
      downloadObj.fileBlob.push(data.data)
    }
  }
  //合并
  const blob = new Blob(downloadObj.fileBlob, {
    type: downloadObj.fileBlob[0].type,
  })
  downloadObj.downloading = false
  //下載
  const link = document.createElement('a')
  link.href = window.URL.createObjectURL(blob)
  link.download = fileName
  link.click()
  window.URL.revokeObjectURL(link.href)
}
</script>
<style lang="scss">
.fsc-slice-upload {
  .upload {
    display: flex;
  }
  .upload-percent {
    margin-top: 10px;
    display: flex;
    .file-name {
      margin-right: 15px;
      max-width: 200px;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
    .el-progress {
      flex: 1;
    }
  }
}
</style>

參考:

http://www.dbjr.com.cn/article/273534.htm

http://www.dbjr.com.cn/article/208589.htm

相關(guān)文章

  • vue3項(xiàng)目如何國(guó)際化實(shí)戰(zhàn)指南

    vue3項(xiàng)目如何國(guó)際化實(shí)戰(zhàn)指南

    像很多大型的網(wǎng)址,特別是跨國(guó)際等公司網(wǎng)頁(yè),訪問(wèn)來(lái)自世界各地用戶(hù),所以網(wǎng)頁(yè)的國(guó)際化極其重要的需求,下面這篇文章主要給大家介紹了關(guān)于vue3項(xiàng)目如何國(guó)際化的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Vue3之狀態(tài)管理器(Pinia)詳解及使用方式

    Vue3之狀態(tài)管理器(Pinia)詳解及使用方式

    這篇文章主要介紹了Vue3之狀態(tài)管理器(Pinia)詳解及使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 基于vue-simplemde實(shí)現(xiàn)圖片拖拽、粘貼功能

    基于vue-simplemde實(shí)現(xiàn)圖片拖拽、粘貼功能

    這篇文章主要介紹了基于vue-simplemde實(shí)現(xiàn)圖片拖拽、粘貼功能,需要的朋友可以參考下
    2018-04-04
  • vue3響應(yīng)式轉(zhuǎn)換常用API操作示例代碼

    vue3響應(yīng)式轉(zhuǎn)換常用API操作示例代碼

    在Vue 3中,響應(yīng)式系統(tǒng)得到了顯著改善,包括使用Composition API時(shí)更加靈活的狀態(tài)管理,這篇文章主要介紹了vue3響應(yīng)式轉(zhuǎn)換常用API操作示例代碼,需要的朋友可以參考下
    2024-08-08
  • vue如何動(dòng)態(tài)實(shí)時(shí)的顯示時(shí)間淺析

    vue如何動(dòng)態(tài)實(shí)時(shí)的顯示時(shí)間淺析

    這篇文章主要給大家介紹了關(guān)于vue如何動(dòng)態(tài)實(shí)時(shí)的顯示時(shí)間,以及vue時(shí)間戳 獲取本地時(shí)間實(shí)時(shí)更新的相關(guān)資料,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • vue項(xiàng)目實(shí)現(xiàn)國(guó)際化的基本思路與詳細(xì)步驟

    vue項(xiàng)目實(shí)現(xiàn)國(guó)際化的基本思路與詳細(xì)步驟

    國(guó)際化是指項(xiàng)目能夠根據(jù)不同國(guó)家的語(yǔ)言進(jìn)行轉(zhuǎn)換,便于不同國(guó)家的用戶(hù)使用,這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目實(shí)現(xiàn)國(guó)際化的基本思路與詳細(xì)步驟,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-04-04
  • vue-cli3配置與跨域處理方法

    vue-cli3配置與跨域處理方法

    這篇文章主要介紹了vue-cli3配置與跨域處理方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • vue設(shè)置全局變量5種方法(讓你的數(shù)據(jù)無(wú)處不在)

    vue設(shè)置全局變量5種方法(讓你的數(shù)據(jù)無(wú)處不在)

    這篇文章主要給大家介紹了關(guān)于vue設(shè)置全局變量的5種方法,通過(guò)設(shè)置的方法可以讓你的數(shù)據(jù)無(wú)處不在,在項(xiàng)目中經(jīng)常會(huì)復(fù)用一些變量和函數(shù),比如用戶(hù)的登錄token,用戶(hù)信息等,這時(shí)將它們?cè)O(shè)為全局的就顯得很重要了,需要的朋友可以參考下
    2023-11-11
  • Vue3中是如何實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式示例詳解

    Vue3中是如何實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式示例詳解

    這篇文章主要介紹了Vue3中是如何實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • vue如何獲取指定元素

    vue如何獲取指定元素

    這篇文章主要介紹了vue如何獲取指定元素,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04

最新評(píng)論