首页 > 其他分享 >Vue 大文件上传和断点续传的实现

Vue 大文件上传和断点续传的实现

时间:2023-12-19 10:11:40浏览次数:33  
标签:断点续传 Vue const chunk file 分片 return 上传

实现 Vue 大文件上传和断点续传需要掌握以下几个步骤:

  1. 分片:将大文件分割成若干个小块,便于上传。一般采用 Blob 对象或 ArrayBuffer 来实现。
  2. 上传:将分片文件上传到服务器。可以使用 XMLHttpRequest、Fetch 等工具进行上传。
  3. 断点续传:如果上传失败或上传过程中断开连接,需要记录已上传的分片,下次上传时跳过已上传的分片。
  4. 合并:上传完成后,将所有分片文件合并成一个完整的文件。

以下是一个例子,展示如何使用 Vue.js 实现大文件上传和断点续传:

  1. 安装依赖

首先需要安装一些依赖,包括 axios、js-sha256 等,可以使用 npm 或 yarn 进行安装:

npm install axios js-sha256
  1. 分片和上传

在前端实现分片和上传时需要考虑以下几个问题:

  • 如何分片:可以根据文件大小或自定义的分片大小进行分割,一般使用 Blob 对象或 ArrayBuffer 储存。
  • 如何上传:可以使用 XMLHttpRequest、Fetch 等工具进行上传,需要在请求头中添加一些信息,如文件名、片数、当前片数、是否续传等。

以下是一个使用 axios 进行上传的示例代码:

const uploadFile = (file) => {
  const chunkSize = 4 * 1024 * 1024; // 4MB
  const fileSize = file.size;
  const chunks = Math.ceil(fileSize / chunkSize);
  const sha256Promise = sha256(file);

  const config = {
    headers: {
      'Content-Type': file.type,
      'X-File-Name': encodeURIComponent(file.name),
      'X-File-Size': fileSize,
      'X-File-Chunks': Math.ceil(fileSize / chunkSize),
      'X-File-Hash': '',
    },
  };

  return sha256Promise.then(hash => {
    config.headers['X-File-Hash'] = hash;

    let promises = [];
    for (let i = 0; i < chunks; i++) {
      const start = i * chunkSize;
      const chunk = file.slice(start, start + chunkSize);

      const formData = new FormData();
      formData.append('chunk', chunk);
      formData.append('chunkNumber', i + 1);
      formData.append('chunksTotal', chunks);

      promises.push(
        axios.post('/api/upload', formData, config)
      );
    }

    return axios.all(promises);
  });
};
  1. 断点续传

在上传过程中发生中断或失败时,需要从中断点处继续上传。为实现断点续传,需要在服务器端记录已上传的分片,前端发送请求时携带这些信息,服务器判断后返回还需要上传哪些分片。如果没有中断,需要检查文件是否已完整上传。

以下是一个使用 axios 进行断点续传的示例代码:

const uploadFile = (file) => {
  const chunkSize = 4 * 1024 * 1024; // 4MB
  const fileSize = file.size;
  const chunks = Math.ceil(fileSize / chunkSize);
  const sha256Promise = sha256(file);

  let config = {
    headers: {
      'Content-Type': file.type,
      'X-File-Name': encodeURIComponent(file.name),
      'X-File-Size': fileSize,
      'X-File-Chunks': Math.ceil(fileSize / chunkSize),
      'X-File-Hash': '',
    },
  };

  const checkUploadedChunks = () => {
    return axios.post('/api/check', {
      filename: encodeURIComponent(file.name),
      filesize: fileSize,
      filechunks: Math.ceil(fileSize / chunkSize),
    }).then(response => {
      return response.data;
    });
  };

  return sha256Promise.then(hash => {
    config.headers['X-File-Hash'] = hash;
    return checkUploadedChunks();
  }).then(uploadedChunks => {
    if (uploadedChunks.length === chunks) {
      return Promise.resolve();
    }

    const formData = new FormData();
    for (let i = 0; i < chunks; i++) {
      if (uploadedChunks.includes(i + 1)) {
        continue;
      }

      const start = i * chunkSize;
      const chunk = file.slice(start, start + chunkSize);

      formData.append('chunk', chunk);
      formData.append('chunkNumber', i + 1);
      formData.append('chunksTotal', chunks);
    }

    return axios.post('/api/upload', formData, config);
  });
};
  1. 合并

当所有分片都上传完成之后,需要在服务器端对这些分片进行合并,生成完整的文件。如果中间有分片上传失败或中断,需要在下次上传时跳过这些分片。

以下是一个使用 Node.js 进行合并的示例代码:

const fs = require('fs');
const path = require('path');

const mergeChunks = (form) => {
  const filename = decodeURIComponent(form.get('filename'));
  const fileSize = form.get('filesize');
  const chunksTotal = form.get('filechunks');
  const tempDir = path.join('temp', filename);

  const chunks = Array.from(form.entries())
    .filter(([key, value]) => key.startsWith('chunk-'))
    .map(([key, value]) => {
      const number = parseInt(key.slice(6), 10);
      const chunkFilePath = path.join(tempDir, `${filename}.${number}`);
      return {
        number,
        filename: chunkFilePath,
        size: value.length,
      };
    })
    .sort((a, b) => a.number - b.number);

  if (chunks.length !== chunksTotal) {
    console.log(`文件[${filename}]分片数量[${chunks.length}]不符要求[${chunksTotal}]`);
    return Promise.reject();
  }

  const writeStream = fs.createWriteStream(path.join('uploads', filename));

  return new Promise((resolve, reject) => {
    let offset = 0;
    const doWrite = () => {
      const chunk = chunks.shift();
      if (!chunk) {
        writeStream.end();
        console.log(`文件[${filename}]已写入`);
        resolve();
        return;
      }

      const readStream = fs.createReadStream(chunk.filename, { highWaterMark: chunk.size });
      readStream.pipe(writeStream, { end: false });
      readStream.on('error', reject);
      readStream.on('end', () => {
        console.log(`分片[${chunk.number}]已写入`);
        offset += chunk.size;
        doWrite();
      });
    };

    doWrite();
  });
};

 

参考文章:http://blog.ncmem.com/wordpress/2023/12/19/vue-%e5%a4%a7%e6%96%87%e4%bb%b6%e4%b8%8a%e4%bc%a0%e5%92%8c%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0%e7%9a%84%e5%ae%9e%e7%8e%b0/

欢迎入群一起讨论

 

 

标签:断点续传,Vue,const,chunk,file,分片,return,上传
From: https://www.cnblogs.com/songsu/p/17912984.html

相关文章

  • vue3中router配置中的children怎么用
    在Vue3中,当你使用VueRouter创建路由配置时,children属性允许你为某个路由定义嵌套路由。这意味着你可以在父路由下设置子路由,从而构建出具有层级结构的URL路径。这里是一个基本的例子,展示了如何在VueRouter中使用children属性:import{createRouter,createWebHistory}from......
  • springboot012响应式企业员工绩效考评系统(vue,毕业设计,附源码和数据库)
    2 关键技术2.1SpringBoot框架2.2 Maven环境2.3Mysql数据库2.4Vue.js框架2.5小结4 系统分析与设计4.1系统架构在对一个系统的开发中,必须全面的考虑用户对学校系统的需求,这个步骤需要开发出系统的功能的用途,每个图应代表系统的一个功能模块。系统架构图:4.2系统功能设......
  • HbuilderX配置Git插件并导入项目和上传代码
    一、安装git从 git官网 下载windows版本的git,然后进行安装,一般不需要只需要默认next就可以了二、安装TortoiseGit工具到 TortoiseGit官网 下载对应的TortoiseGit包进行安装【一般使用默认设置即可:一路next下去,TortoiseGit安装完毕!】三、安装TortoiseGit-LanguagePack中文语言......
  • springboot011流浪动物救助网站(vue)
    4需求分析4.1需求调研在教学网站中,能够在网站上运行的流浪动物救助站系统较少,所有很有必要开发一个轻量级的可以在网站上运行的流浪动物救助网站系统。4.2功能模块需求分析本网站最大的特点就功能全面,且结构简单用户在圈子信息框中,用户可以发布信息系统管理员:4.3设计的基本思想4.......
  • [Vue] vue学习笔记(11): 自定义事件 & 全局事件总线
    组件的自定义事件通过props可以将信息传递给子组件,那么当子组件需要向上传递信息的时候呢,除了使用props传递函数类的方法,我们还可以用自定义事件通过父组件给子组件绑定一个事件someEvent//App.vue<Student@someEvent='getStudentName'/>//methodsmethods:{ getStu......
  • 从零开始封装 vue 组件
    对于学习Vue的同学来说,封装vue组件是实现代码复用的重要一环。在Vue官网中非常详细地介绍了vue组件的相关知识,我这里简单摘取使用最频繁的几个知识点,带大家快速入门vue组件的使用。快速入门我们假设在页面上有很多地方都要用到一个计数器,与其在每个地方都实现计数器功......
  • C#文件上传和下载
    一.  在Form中一定要将encType设为"multipart/form-data": <formid="WebForm3"method="post"encType="multipart/form-data"runat="server"> 二.  判断是否有文件上传了: 当用户没有选择任何要上传的文件,即HtmlInputFile控件中的文本框为空时点击了上传按钮后,在服务......
  • VUE前端实现视频截图并上传到服务器
    做视频上传的时候有时候需要上传预览图,后端一般可以用FFMPEG来实现,前端也可以直接截图,这个功能不需要后台实现,VUE前端利用canvas画图,然后转换Base64就可以完成。1.前端代码<el-form-itemlabel="视频地址"requiredprop="videoURL"><el-upload:action="upload......
  • Vue知识系列(2)每天10个小知识点
    @TOC......
  • C#文件上传与下载
    上传文件<inputtype="file"id="file"/><inputtype="button"id="upload"value="上传文件"/><script>//上传$("#upload").click(function(){varformData=newFormData();......