Vue中实现大文件的切片并发下载和下载进度展示

Vue中实现大文件的切片下载

切片下载需要后端提供两个接口,第一个接口用来获取当前下载文件的总切片数,第二个接口用来获取具体某一个切片的内容。

界面展示

Vue中实现大文件的切片并发下载和下载进度展示_第1张图片

数据流展示

Vue中实现大文件的切片并发下载和下载进度展示_第2张图片

代码

接口

// 切片下载-获取文件的总切片数
export function getChunkDownloadMetaInfo(queryParams) {
  return request({
    url: `/resource/chunkDownloadMetaInfoByUrl`,
    method: 'get',
    params: queryParams
  })
}

// 切片下载-获取当前切片的文件内容
export function getChunkDownload(queryParams) {
  return request({
    url: `/resource/chunkDownloadByUrl`,
    method: 'get',
    headers: {
      'Content-Type': 'application/json; application/octet-stream'
    },
    responseType: 'blob', //响应数据格式配置
    params: queryParams
  })
}

下载

// 下载
const downloadItem = async item => {
  const fileName = item.fileName
  const fileUrl = item.fileUrl
  if (item.isDownloading) {
    ElMessage({
      type: 'error',
      message: `${fileName}正在下载中请稍等`
    })
    return
  }
  item.progress = 0 // 下载进度
  item.isDownloading = true // 是否正在下载
  item.isAborted = false // 是否中止下载
  try {
    // 获取文件信息
    const fileInfo = await getFileInfo(fileUrl)
    const totalChunks = fileInfo.totalChunkNum

    // 并发下载所有切片
    const downloadedChunks = await downloadWithConcurrency(fileUrl, fileName, totalChunks, item)

    // 合并并下载文件
    const mergedBlob = mergeBlobs(downloadedChunks)
    triggerDownload(mergedBlob, fileName)
  } catch (error) {
    console.error('下载失败:', error)
    ElMessage({
      type: 'error',
      message: `${fileName}文件下载失败`
    })
  } finally {
    item.isDownloading = false
  }
}
// 获取文件信息
const getFileInfo = async fileUrl => {
  try {
    const response = await getChunkDownloadMetaInfo({ url: fileUrl })
    return response.data
  } catch (error) {
    throw new Error(`获取文件信息失败: ${error.message}`)
  }
}
// 下载单个切片
const downloadChunk = async (fileUrl, fileName, chunkIndex) => {
  try {
    const response = await getChunkDownload({
      url: fileUrl,
      chunkNumber: chunkIndex
    })
    return response
  } catch (error) {
    throw new Error(`下载切片失败: ${error.message}`)
  }
}
// 合并 Blob
const mergeBlobs = blobs => {
  return new Blob(blobs)
}
// 触发文件下载
const triggerDownload = (blob, fileName) => {
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = fileName
  a.click()
  URL.revokeObjectURL(url)
}
// 并发下载
const downloadWithConcurrency = async (fileUrl, fileName, totalChunks, item) => {
  const downloadedChunks = new Array(totalChunks).fill(null)
  let completedChunks = 0

  const downloadNextChunk = async chunkIndex => {
    if (item.isAborted) return // 如果已中止,直接返回

    try {
      const chunk = await downloadChunk(fileUrl, fileName, chunkIndex)
      console.log(chunk, '123123123123123123')
      downloadedChunks[chunkIndex] = chunk

      // 更新下载进度
      completedChunks++
      item.progress = Math.round((completedChunks / totalChunks) * 100)
    } catch (error) {
      item.isAborted = true // 中止下载
      throw error
    }
  }

  // 启动并发下载
  const workers = []
  for (let i = 0; i < totalChunks; i++) {
    if (item.isAborted) break // 如果已中止,停止启动新任务

    if (workers.length >= maxConcurrency) {
      // 等待一个任务完成后再启动新任务
      await Promise.race(workers)
    }

    const worker = downloadNextChunk(i).finally(() => {
      workers.splice(workers.indexOf(worker), 1)
    })
    workers.push(worker)
  }

  await Promise.all(workers) // 等待所有任务完成
  return downloadedChunks
}

你可能感兴趣的:(js,vue,vue.js,前端,javascript)