vue 大文件分片上传(断点续传、并发上传、秒传)

Vue 大文件分片上传是前端文件上传中常见的解决方案之一,用于解决大文件上传时可能遇到的性能和稳定性问题。常见的性能问题包括上传时间过长、上传失败等,而稳定性问题则是在上传过程中可能因为网络原因导致上传失败,需要支持断点续传。

  1. 什么是文件分片上传?
    文件分片上传是指将大文件分成多个较小的文件片段进行上传,上传完成后再将这些片段组合成完整的文件。这样做的好处是文件较小、上传速度较快,并且上传过程中在网络环境不佳时也具有更好的稳定性,一旦上传失败可以从上次失败的位置继续上传,实现断点续传。

  2. Vue 大文件分片上传步骤
    Vue 大文件分片上传的步骤分为以下几个:

2.1 文件切片
将文件分成大小一致的片段,并生成唯一标识符和上传地址等信息,示例代码如下:

function createChunks (file, chunk_size) {
  const chunks = []
  let start = 0
  let end = 0
  let index = 0
  while (start < file.size) {
    end = start + chunk_size
    chunks.push({
      index: index,
      total: Math.ceil(file.size / chunk_size),
      blob: file.slice(start, end),
      name: file.name,
      uid: `${file.name}_${index}_${file.size}_${Date.now()}`
    })
    index++
    start = end
  }
  return chunks
}

2.2 上传文件片段
将文件片段通过 AJAX 异步上传到服务器,并在上传完成后触发上传成功回调函数,示例代码如下:

async function uploadChunk ({ url, data, progressCallback }) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.withCredentials = true
    xhr.open('post', url, true)
    xhr.setRequestHeader('Content-Type', 'application/octet-stream')
    xhr.upload.onprogress = function (event) {
      if (event.total > 0) {
        event.percent = (event.loaded / event.total) * 100
      }
      progressCallback && progressCallback(event)
    }
    xhr.onload = function () {
      if (xhr.status === 200) {
        resolve(xhr.responseText)
      } else {
        reject(new Error('上传失败'))
      }
    }
    xhr.onerror = function (err) {
      reject(new Error('上传失败'))
    }
    xhr.send(data)
  })
}

2.3 流程控制
使用 Promise.all 控制上传过程,等待所有文件片段上传完成后再触发上传完成回调函数,示例代码如下:

Promise.all(chunks.map((chunk) => {
  return uploadChunk({
    url: upload_url,
    data: chunk.blob,
    progressCallback: updateProgress
  })
})).then((res) => {
  console.log('文件上传成功')
  // 完整的文件上传
  checkFile()
}).catch((err) => {
  console.error(err.message)
})

2.4 校验文件完整性
将所有文件片段的 md5 值上传到服务器,服务器根据 md5 值验证文件完整性,如果文件完整,则将文件合并;如果文件不完整,则删除已上传的文件片段,示例代码如下:

async function checkFile () {
  const md5Promises = chunks.map((chunk) => {
    return createFileMd5(chunk.blob)
  })
  const md5Array = await Promise.all(md5Promises)
  const md5 = md5Array.join('')
  console.log('文件MD5值为', md5)
  // 校验文件是否完整
  const res = await checkFileApi({
    md5: md5,
    size: file.size,
    name: file.name
  })
  if (res.code === 0 && res.data && res.data.uploaded) {
    console.log('秒传文件')
    // 秒传
  } else {
    console.log('开始合并文件')
    // 合并文件
    mergeChunks(md5)
  }
}

2.5 合并文件
将已上传的文件片段按照顺序合并成完整的文件,并触发文件上传完成回调函数,示例代码如下:

async function mergeChunks (md5) {
  const res = await mergeFileApi({
    md5: md5,
    name: file.name,
    chunks
  })
  if (res.code === 0) {
    console.log('文件上传成功')
    // 文件上传成功
  } else {
    console.error(res.message)
  }
}
  1. 示例说明
    下面给出两个示例说明:

3.1 Vue 大文件分片上传组件

<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <el-button type="primary" @click="handleUpload">上传</el-button>
    <div class="progress">
      <el-progress :percentage="progress" :stroke-width="3"/>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      file: null,
      chunks: [],
      upload_url: 'https://example.com/api/upload',
      progress: 0
    }
  },
  methods: {
    handleFileChange (e) {
      this.file = e.target.files[0]
      this.chunks = createChunks(this.file, 2 * 1024 * 1024) // 按2MB切片
    },
    handleUpload () {
      Promise.all(this.chunks.map((chunk) => {
        return uploadChunk({
          url: this.upload_url,
          data: chunk.blob,
          progressCallback: this.updateProgress
        })
      })).then((res) => {
        console.log('文件上传成功')
        checkFile()
      }).catch((err) => {
        console.error(err.message)
      })
    },
    async checkFile () {
      const md5Promises = this.chunks.map((chunk) => {
        return createFileMd5(chunk.blob)
      })
      const md5Array = await Promise.all(md5Promises)
      const md5 = md5Array.join('')
      console.log('文件MD5值为', md5)

      const res = await checkFileApi({
        md5: md5,
        size: this.file.size,
        name: this.file.name
      })
      if (res.code === 0 && res.data && res.data.uploaded) {
        console.log('秒传文件')
        // 秒传
      } else {
        console.log('开始合并文件')
        // 合并文件
        mergeChunks(md5)
      }
    },
    async mergeChunks (md5) {
      const res = await mergeFileApi({
        md5: md5,
        name: this.file.name,
        chunks: this.chunks
      })
      if (res.code === 0) {
        console.log('文件上传成功')
        // 文件上传成功
      } else {
        console.error(res.message)
      }
    },
    updateProgress (progressEvent) {
      this.progress = Math.ceil(progressEvent.percent)
    }
  }
}
</script>

3.2 上传进度条组件

<template>
  <div class="progress-wrapper">
    <div class="progress-bar" :style="{ width: percent + '%' }"></div>
  </div>
</template>

<script>
export default {
  props: {
    url: {
      type: String,
      required: true
    },
    data: {
      type: Object,
      required: true
    },
    fileName: {
      type: String,
      required: true
    },
    chunk: {
      type: Blob,
      required: true
    }
  },
  data () {
    return {
      percent: 0
    }
  },
  methods: {
    uploadChunk () {
      const xhr = new XMLHttpRequest()
      xhr.open('post', this.url, true)
      xhr.setRequestHeader('Content-Type', 'application/octet-stream')
      xhr.upload.onprogress = (e) => {
        if (e.lengthComputable) {
          this.percent = Math.ceil((e.loaded / e.total) * 100)
        }
      }
      xhr.onreadystatechange = () => {
        if (xhr.readyState === 4) {
          if (xhr.status === 200) {
            console.log(xhr.responseText)
          } else {
            console.log('上传失败')
          }
        }
      }
      const formData = new FormData()
      formData.append('file', this.chunk, `${this.fileName}_${this.data.index}`)
      formData.append('index', String(this.data.index))
      formData.append('total', String(this.data.total))
      formData.append('name', this.fileName)
      formData.append('uid', this.data.uid)
      xhr.send(formData)
    }
  },
  mounted () {
    this.uploadChunk()
  }
}
</script>

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:vue 大文件分片上传(断点续传、并发上传、秒传) - Python技术站

(0)
上一篇 2023年5月28日
下一篇 2023年5月28日

相关文章

  • Vue3 JSX解释器的实现

    下面是“Vue3 JSX解释器的实现”的完整攻略。 1. 了解JSX JSX是一种JavaScript的语法扩展,它可以在JavaScript代码中直接嵌入XML标签和属性,并使用类似HTML的语法格式。Vue3支持使用JSX语法来定义组件模板,其主要实现方式是通过JSX转换器将JSX语法转换为普通的JavaScript语法。在实现Vue3 JSX解释器之前…

    Vue 2023年5月27日
    00
  • vue.js实例对象+组件树的详细介绍

    “Vue.js实例对象+组件树的详细介绍”是Vue.js框架的基础内容之一,它关乎着构建整个Vue.js应用程序的基本理解。在本文中,我将详细介绍Vue.js实例对象和组件树的概念,以及如何创建和使用它们。 Vue.js实例对象 Vue.js实例对象是一个VM(ViewModel)的实例,VM是Vue.js框架的核心概念,它也是MVVM(Model-View…

    Vue 2023年5月28日
    00
  • 详解Vue与VueComponent的关系

    详解 Vue 与 Vue Component 的关系 Vue 是一款流行的前端框架,使用 Vue 可以方便地实现动态数据绑定、组件化以及声明式渲染等功能。而 Vue Component 则是 Vue 框架中组件的概念。本文将详细讲解 Vue 和 Vue Component 的关系,并通过两条示例说明。 Vue 和 Vue Component 的关系 Vue …

    Vue 2023年5月27日
    00
  • vue双向数据绑定原理分析、vue2和vue3原理的不同点

    Vue的双向数据绑定是Vue中最重要的主要概念之一。它是Vue框架的一个核心特性,使得Vue应用具有了响应性和高效性。在这里,我们将详细讲解Vue双向数据绑定的原理以及Vue 2和Vue 3原理的不同点。 Vue双向数据绑定原理分析 Vue的双向数据绑定可以定义为:当数据模型变化时,视图会自动更新;当视图变化时,数据模型也会自动更新。这种自动化的数据绑定机制…

    Vue 2023年5月28日
    00
  • Vue首屏性能优化组件知识点总结

    当我们开发网站时,保证首屏性能优化是一个非常重要的问题。在Vue的开发中,也存在一些优化策略和技术,来帮助我们优化网站的首屏性能,其中组件是一个比较重要的方面。以下是Vue首屏性能优化组件知识点总结的完整攻略。 1. 异步组件 Vue允许我们将组件代码进行异步加载,这可以帮助我们解决首屏加载慢的问题。可以采用以下办法: 1.1 使用vue-cli创建项目时,…

    Vue 2023年5月28日
    00
  • JS简单实现父子窗口传值功能示例【未使用iframe框架】

    下面是对“JS简单实现父子窗口传值功能示例【未使用iframe框架】”的详细攻略: 1. 基本实现原理 在父窗口中,定义一个变量保存需要传递的数据 在父窗口中,定义一个函数,该函数将需要传递的数据作为参数传递给子窗口 在子窗口中,定义一个变量保存从父窗口传递来的数据 在子窗口中,通过父窗口定义的函数来接收从父窗口传递来的数据 2. 实现过程示例 2.1 示例…

    Vue 2023年5月28日
    00
  • Vue路由钩子之afterEach beforeEach的区别详解

    Vue路由钩子之afterEach beforeEach的区别详解 在Vue中,路由钩子是非常重要的一部分,他们可以在路由发生变化的时候执行一些动作。Vue提供了两种不同的路由钩子:beforeEach和afterEach。他们两个之间有什么区别呢?下面就详细进行讲解。 beforeEach beforeEach是你可以使用的一种路由钩子。在路由跳转之前,b…

    Vue 2023年5月27日
    00
  • vue mounted组件的使用

    下面是关于“vue mounted组件的使用”的完整攻略。 什么是mounted? 在Vue组件的生命周期中,mounted是一个非常重要的生命周期钩子函数。当组件被挂载到DOM中后,mounted函数会被调用,表示组件已经完全被加载到页面上,并且模板中的所有DOM元素已经生成。 使用方法 使用mounted非常简单,只需要在Vue组件的选项中添加一个mou…

    Vue 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部