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

yizhihongxing

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日

相关文章

  • vue-resource:jsonp请求百度搜索的接口示例

    关于“vue-resource:jsonp请求百度搜索的接口示例”的完整攻略,主要分为以下四步: 1.引入vue-resource库通过npm或者CDN的方式引入vue-resource库,使其可以在项目中被使用。具体代码为: <!– 使用CDN引入vue-resource –> <script src="https://cd…

    Vue 2023年5月28日
    00
  • vue项目前端知识点整理【收藏】

    “vue项目前端知识点整理【收藏】”是一份前端知识点的整理,方便初学者或者需要查漏补缺的开发者来学习和参考。该文档主要涵盖了vue开发中常见的知识点和技能,包括vue基础语法、vue组件及其通信、vue路由、vue状态管理、vue服务端渲染等。 下面,我将对其中几个重要的知识点进行详细讲解: Vue组件及其通信 Vue组件是Vue工程中的基本单元,我们可以通…

    Vue 2023年5月27日
    00
  • Vue动态类的几种使用方法总结

    Vue动态类的几种使用方法总结 在Vue中,我们可以通过动态绑定class来实现根据条件来动态添加/删除对应的类,这也是实现复杂的样式变化的常用方式。本文将总结Vue中动态绑定class的几种使用方法,并提供相应的示例说明。 1. 对象语法 最基础的动态绑定class的方式是采用对象语法,其基本格式为: <div :class="{ clas…

    Vue 2023年5月28日
    00
  • vue组件中使用props传递数据的实例详解

    那么就开始详细讲解“vue组件中使用props传递数据的实例详解”吧。 什么是props 在Vue中,父组件可以通过props向子组件传递数据,子组件接收props后在组件内部使用这些数据。props是short for“properties”,它可以传递任何类型的数据,包括字符串、数字、数组、对象等等。 基本用法示例 假设我们有一个父组件parent和一个…

    Vue 2023年5月27日
    00
  • Vue路由跳转方式区别汇总(push,replace,go)

    Vue路由跳转方式区别汇总(push,replace,go) Vue路由提供了多种方式实现页面间的跳转,其中包括路由的跳转、前进、后退等。在进行路由跳转时,我们通常会使用3种方式,包括push、replace和go。 push push是将目标路由地址添加到路由历史记录中,此时可以通过浏览器的后退按钮回退到上一个页面。同时使用push方式跳转,可以利用par…

    Vue 2023年5月27日
    00
  • 一篇文章带你吃透Vuex3的状态管理

    一篇文章带你吃透Vuex3的状态管理 什么是Vuex Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理各组件共享的一些状态,使得状态管理变得简单而且容易理解。它的主要作用是用于管理Vue.js应用中的各种状态。 Vuex的结构 Vuex包含5个部分: State:用于存储状态变量 Mutation:用于改变state中的值 Ac…

    Vue 2023年5月27日
    00
  • vue项目实现图片上传功能

    下面是实现Vue项目图片上传功能的完整攻略: 准备工作 在开始实现图片上传功能之前,我们需要先进行几项准备工作。首先,需要确定你使用的Vue版本是否支持vue-cli3,如果不支持,需要先安装一个支持vue-cli3的Vue版本。其次,需要安装一个Vue插件——vue-uploader,以便我们更方便地完成图片上传功能开发。 安装Vue-cli3 Vue-c…

    Vue 2023年5月28日
    00
  • vue.js实现图片压缩封装方法

    下面我来详细讲解vue.js实现图片压缩封装方法的完整攻略。 1. 前置知识 在开始编写图片压缩封装方法之前,需要掌握以下前置知识: JavaScript基础知识 Vue.js基础知识 HTML5中的Canvas API 2. 实现步骤 下面是实现图片压缩封装方法的步骤: 2.1. 安装插件 首先需要安装compressorjs插件,该插件可以实现图片压缩的…

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