广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >JavaScript大文件上传的处理方法之切片上传
  • 242
分享到

JavaScript大文件上传的处理方法之切片上传

2024-04-02 19:04:59 242人浏览 薄情痞子
摘要

目录前言切片后上传生成hash文件秒传暂停上传中断请求示例添加暂停上传功能恢复上传添加功能总结前言 本篇介绍了切片上传的基本实现方式(前端),以及实现切片上传后的一些附加功能,切片上

前言

本篇介绍了切片上传的基本实现方式(前端),以及实现切片上传后的一些附加功能,切片上传原理较为简单,代码注释比较清晰就不多赘述了,后面的附加功能介绍了实现原理,并贴出了在原本代码上的改进方式。有什么错误希望大佬可以指出,感激不尽。

切片后上传

切片上传的原理较为简单,即获取文件后切片,切片后整理好每个切片的参数并发请求即可。

下面直接上代码:

HTML

<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <el-button @click="handleUpload">上传</el-button>
  </div>
</template>

JavaScript

<script>
const SIZE = 10 * 1024 * 1024; // 切片大小

export default {
  data: () => ({
    // 存放文件信息
    container: {
      file: null
      hash: null
    },
    data: [] // 用于存放加工好的文件切片列表
    hashPercentage: 0 // 存放hash生成进度
  }),
  methods: {
    // 获取上传文件
    handleFileChange(e) {
      const [file] = e.target.files;
      if (!file) {
        this.container.file = null;
        return;
      }
      this.container.file = file;
    },
        
    // 生成文件切片
    createFileChunk(file, size = SIZE) {
     const fileChunkList = [];
      let cur = 0;
      while (cur < file.size) {
        fileChunkList.push({ file: file.slice(cur, cur + size) });
        cur += size;
      }
      return fileChunkList;
    },
        
    // 生成文件hash    
    calculateHash(fileChunkList) {
      return new Promise(resolve => {
        this.container.worker = new Worker("/hash.js");
        this.container.worker.postMessage({ fileChunkList });
        this.container.worker.onmessage = e => {
          const { percentage, hash } = e.data;
          // 可以用来显示进度条
          this.hashPercentage = percentage;
          if (hash) {
            resolve(hash);
          }
        };
      });
    },

   	// 切片加工(上传前预处理 为文件添加hash等)
    async handleUpload() {
      if (!this.container.file) return;
      // 切片生成
      const fileChunkList = this.createFileChunk(this.container.file);
      // hash生成
      this.container.hash = await this.calculateHash(fileChunkList);
      this.data = fileChunkList.map(({ file },index) => ({
        chunk: file,
        // 这里的hash为文件名 + 切片序号,也可以用md5对文件进行加密获取唯一hash值来代替文件名
        hash: this.container.hash + "-" + index
      }));
      await this.uploadChunks();
    }
      
    // 上传切片
   	async uploadChunks() {
     const requestList = this.data
     	// 构造fORMData
       .map(({ chunk,hash }) => {
         const formData = new FormData();
         formData.append("chunk", chunk);
         formData.append("hash", hash);
         formData.append("filename", this.container.file.name);
         return { formData };
       })
     	// 发送请求 上传切片
       .map(async ({ formData }) =>
       	request(formData)
       );
     await Promise.all(requestList); // 等待全部切片上传完毕
     await merge(this.container.file.name) // 发送请求合并文件
   	},
  }
};
</script>

生成hash

无论是前端还是服务端,都必须要生成文件和切片的 hash,之前我们使用文件名 + 切片下标作为切片 hash,这样做文件名一旦修改就失去了效果,而事实上只要文件内容不变,hash 就不应该变化,所以正确的做法是根据文件内容生成 hash,所以我们修改一下 hash 的生成规则

这里用到另一个库 spark-md5,它可以根据文件内容计算出文件的 hash 值,另外考虑到如果上传一个超大文件,读取文件内容计算 hash 是非常耗费时间的,并且会引起 UI 的阻塞,导致页面假死状态,所以我们使用 WEB-worker 在 worker 线程计算 hash,这样用户仍可以在主界面正常的交互

由于实例化 web-worker 时,参数是一个 js 文件路径且不能跨域,所以我们单独创建一个 hash.js 文件放在 public 目录下,另外在 worker 中也是不允许访问 dom 的,但它提供了importScripts`函数用于导入外部脚本,通过它导入 spark-md5

// /public/hash.js
self.importScripts("/spark-md5.min.js"); // 导入脚本
// 生成文件 hash
self.onmessage = e => {
  const { fileChunkList } = e.data;
  const spark = new self.SparkMD5.ArrayBuffer();
  let percentage = 0;
  let count = 0;
  const loadNext = index => {
    // 新建读取器
    const reader = new FileReader();
    // 设定读取数据格式并开始读取
    reader.readAsArrayBuffer(fileChunkList[index].file);
    // 监听读取完成
    reader.onload = e => {
      count++;
      // 获取读取结果并交给spark计算hash
      spark.append(e.target.result);
      if (count === fileChunkList.length) {
        self.postMessage({
          percentage: 100,
          // 获取最终hash
          hash: spark.end()
        });
        self.close();
      } else {
        percentage += 100 / fileChunkList.length;
        self.postMessage({
          percentage
        });
        // 递归计算下一个切片
        loadNext(count);
      }
    };
  };
  loadNext(0);
};

小结

  • 获取上传文件
  • 文件切片后存入数组 fileChunkList.push({ file: file.slice(cur, cur + size) });
  • 生成文件hash(非必须)
  • 根据文件切片列表生成请求列表
  • 并发请求
  • 待全部请求完成后发送合并请求

文件秒传

实际是障眼法,用来欺骗用户的。

原理:在文件上传之前先计算出文件的hash,然后发送给后端进行验证,看后端是否存在这个hash,如果存在,则证明这个文件上传过,则直接提示用户秒传成功

// 切片加工(上传前预处理 为文件添加hash等)
async handleUpload() {
  if (!this.container.file) return;
  // 切片生成
  const fileChunkList = this.createFileChunk(this.container.file);
  // hash生成
  this.container.hash = await this.calculateHash(fileChunkList);
    
  // hash验证 (verify为后端验证接口请求)
  const { haveExisetd } = await verify(this.container.hash)
  // 判断
  if(haveExisetd) {
  	this.$message.success("秒传:上传成功") 
    return   
  } 
   
  this.data = fileChunkList.map(({ file },index) => ({
    chunk: file,
    // 这里的hash为文件名 + 切片序号,也可以用md5对文件进行加密获取唯一hash值来代替文件名
    hash: this.container.hash + "-" + index
  }));
  await this.uploadChunks();
}

暂停上传

原理:将所有的切片存在一个数组中,每当一个切片上传完毕,从数组中移除,这样就可以实现用一个数组只保存上传中的文件。此外,因为要暂停上传,所以需要中断请求 axiOS中断请求可以利用AbortController

中断请求示例

const controller = new AbortController()
axios({
  signal: controller.signal
}).then(() => {});
// 取消请求
controller.abort()

添加暂停上传功能

// 上传切片
async uploadChunks() {
 // 需要把requestList放到全局,因为要通过操控requestList来实现中断
 this.requestList = this.data
 	// 构造formData
   .map(({ chunk,hash }) => {
     const formData = new FormData();
     formData.append("chunk", chunk);
     formData.append("hash", hash);
     formData.append("filename", this.container.file.name);
     return { formData };
   })
 	// 发送请求 上传切片
   .map(async ({ formData }, index) =>
     request(formData).then(() => {
       // 将请求成功的请求剥离出requestList
       this.requestList.splice(index, 1)
     })
   );
 await Promise.all(this.requestList); // 等待全部切片上传完毕
 await merge(this.container.file.name) // 发送请求合并文件
},
// 暂停上传   
handlePause() {
	this.requestList.forEach((req) => {
        // 为每个请求新建一个AbortController实例
     	const controller = new AbortController();
        req.signal = controller.signal
        controller.abort()
    })
}

恢复上传

原理:上传切片之前,向后台发送请求,接口将已上传的切片列表返回,通过切片hash将后台已存在的切片过滤,只上传未存在的切片

// 切片加工(上传前预处理 为文件添加hash等)
async handleUpload() {
  if (!this.container.file) return;
  // 切片生成
  const fileChunkList = this.createFileChunk(this.container.file);
  // 文件hash生成
  this.container.hash = await this.calculateHash(fileChunkList);
  // hash验证 (verify为后端验证接口请求)
  const { haveExisetd, uploadedList } = await verify(this.container.hash)
  // 判断
  if(haveExisetd) {
  	this.$message.success("秒传:上传成功") 
    return   
  } 
  this.data = fileChunkList.map(({ file },index) => ({
    chunk: file,
    // 注:这个是切片hash   这里的hash为文件名 + 切片序号,也可以用md5对文件进行加密获取唯一hash值来代替文件名
    hash: this.container.hash + "-" + index
  }));
  await this.uploadChunks(uploadedList);
}
// 上传切片
async uploadChunks(uploadedList = []) {
 // 需要把requestList放到全局,因为要通过操控requestList来实现中断
 this.requestList = this.data
    // 过滤出来未上传的切片
   .filter(({ hash }) => !uploadedList.includes(hash))
 	// 构造formData
   .map(({ chunk,hash }) => {
     const formData = new FormData();
     formData.append("chunk", chunk);
     formData.append("hash", hash);
     formData.append("filename", this.container.file.name);
     return { formData };
   })
 	// 发送请求 上传切片
   .map(async ({ formData }, index) =>
     request(formData).then(() => {
       // 将请求成功的请求剥离出requestList
       this.requestList.splice(index, 1)
     })
   );
 await Promise.all(this.requestList); // 等待全部切片上传完毕
 // 合并之前添加一层验证 验证全部切片传送完毕
 if(uploadedList.length + this.requestList.length == this.data.length){
	await merge(this.container.file.name) // 发送请求合并文件
 }
},
// 暂停上传   
handlePause() {
	this.requestList.forEach((req) => {
        // 为每个请求新建一个AbortController实例
     	const controller = new AbortController();
        req.signal = controller.signal
        controller.abort()
    })
}
// 恢复上传
async handleRecovery() {
    //获取已上传切片列表 (verify为后端验证接口请求)
	const { uploadedList } = await verify(this.container.hash)
    await uploadChunks(uploadedList)
}

添加功能总结

  • 1.文件秒传其实就是一个简单的验证,把文件的hash发送给后端,后端验证是否存在该文件后将结果返回,如果存在则提示文件秒传成功
  • 2.断点传送分为两步,暂停上传和恢复上传。暂停上传是通过获取到未上传完毕切片列表(完整切片列表剥离请求已完成的切片后形成),对列表请求进行请求中断实现的。恢复上传实质也是一层验证,在上传文件之前,将文件的hash发送给后端,后端返回已经上传完毕的切片列表,然后根据切片hash将后端返回的切片列表中的切片过滤出去,只上传未上传完成的切片。

到此这篇关于javascript大文件上传的处理方法之切片上传的文章就介绍到这了,更多相关JS切片上传内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: JavaScript大文件上传的处理方法之切片上传

本文链接: https://www.lsjlt.com/news/152839.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JavaScript大文件上传的处理方法之切片上传
    目录前言切片后上传生成hash文件秒传暂停上传中断请求示例添加暂停上传功能恢复上传添加功能总结前言 本篇介绍了切片上传的基本实现方式(前端),以及实现切片上传后的一些附加功能,切片上...
    99+
    2022-11-13
  • JavaScript实现大文件分片上传处理
    很多时候我们在处理文件上传时,如视频文件,小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题: 1、文件过大,超出服务端的请求大小限制; 2、请求时间过长...
    99+
    2022-11-12
  • JavaScript怎么实现大文件分片上传处理
    这篇文章主要介绍“JavaScript怎么实现大文件分片上传处理”,在日常操作中,相信很多人在JavaScript怎么实现大文件分片上传处理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JavaScript怎...
    99+
    2023-06-20
  • 如何使用大文件上传:秒传、断点续传、分片上传方法
    本篇内容介绍了“如何使用大文件上传:秒传、断点续传、分片上传方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!秒传1、什么是秒传通俗的说,你...
    99+
    2023-06-15
  • Asp.Net上传文件并配置可上传大文件的方法
    ASP.NET 包含两个控件可以使用户向网页服务器上传文件。一旦服务器接受了上传的文件数据,那么应用程序就可以进行保存,进行检查或者忽略它。 HtmlInputFile -...
    99+
    2022-11-13
  • el-upload大文件切片上传怎么实现
    这篇文章主要介绍“el-upload大文件切片上传怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“el-upload大文件切片上传怎么实现”文章能帮助大家解决问题。html<el-upl...
    99+
    2023-07-05
  • springboot大文件上传、分片上传、断点续传、秒传的实现
    对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达...
    99+
    2022-11-13
  • java实现文件切片上传百度云+断点续传的方法
    前言: 本文代码通过dubbo进行远程调用的接口,如果不使用dubbo,直接将service放到你的service,并稍作修改,redis替换成自己封装的工具即可。下方代码有点多,但...
    99+
    2022-11-12
  • 浏览器中处理上传大文件——FileReader.readAsArrayBuffer()方法
    1.问题点 平常从客户端上传文件到服务器端,只需要读取 input 的 File 对象,然后将其塞到 FormData 对象中,然后使用 ajax 发送到服务器端,服务器端会有配套的读文件和写文件的操...
    99+
    2023-09-03
    javascript 服务器 开发语言
  • el-upload大文件切片上传实现示例详解
    目录背景htmljs备注背景 前端上传大文件时,会出现跨域错误等各类错误,另切片上传可以提高上传效率。固进行以下代码记录,方便后期cv() html <el-upload a...
    99+
    2023-03-15
    el upload大文件切片上传 el upload文件上传
  • vue 文件切片上传的项目实现
    目录流程简说获取文件的 MD5 唯一标识码文件切片获取文件名 name分片文件大小 chunkSize文件切片 chunkList 列表切片总数 chunks切片大小 size合并在...
    99+
    2023-03-24
    vue 文件切片上传 vue 大文件上传
  • 文件上传之Webshell连接方法
    目录 基础 攻击流程 大小马 常用Webshell 简单一句话木马webshell GET方法 POST方法  Cookie  蚁剑连接Webshell Webshell就是以asp.php.jsp或者cgi等网页文件形式存在的...
    99+
    2023-09-03
    网络安全 web安全
  • Vue项目使用Websocket大文件FileReader()切片上传实例
    目录使用技术 upfile.js文件新增需求:对上传文件流进行加密,并传给后端做验证还是在upfile.js文件(也可以单独放一个文件)大文件上传,本地1.3G文件不到一分...
    99+
    2022-11-13
  • php文件上传大小限制的方法
    这篇文章主要介绍了php文件上传大小限制的方法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。php有什么特点1、执行速度快。2、具有很好的开放性和可扩展性。3、PHP支持多种...
    99+
    2023-06-14
  • php无法上传大文件的解决方法
    这篇“php无法上传大文件的解决方法”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“php无法上传大文件的解决方法”,小编整理了以下知识点,请大家跟着小编的步伐一步一步的慢慢理解,接下来就让我们...
    99+
    2023-06-06
  • 前端大文件上传与下载(分片上传)的详细过程
    目录一、问题二、解决1.第一步选择文件2.校验文件是否符合规范3.文件切片上传4.分片上传注意点5.大文件下载总结一、问题 日常业务中难免出现前端需要向后端传输大型文件的情况,这时单...
    99+
    2022-11-13
    前端文件上传和下载 前端上传文件 前端大文件上传
  • JavaScript Uploadify文件上传的方法是什么
    JavaScript Uploadify的文件上传方法可以通过以下步骤实现:1. 引入jQuery库和Uploadify插件的相关文...
    99+
    2023-08-18
    JavaScript
  • vue文件切片上传的项目怎么实现
    这篇文章主要介绍“vue文件切片上传的项目怎么实现”,在日常操作中,相信很多人在vue文件切片上传的项目怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue文件切片上传的项目怎么实现”的疑惑有所帮助!...
    99+
    2023-07-05
  • Java服务器处理图片上传的方法
    本文实例为大家分享了Java服务器处理图片上传的具体代码,供大家参考,具体内容如下 一、简述 第一:浏览器上传图片实现; 第二:微信小程序上传图片实现; 二、图片上传功能实现 1.处...
    99+
    2022-11-13
  • js自己实现一个大文件切片上传+断点续传的示例代码
    目录首先我们来分析一下需求一、 格式校验二、 文件切片三、 断点续传 + 秒传 + 上传进度PM:喂,那个切图仔,我这里有个100G的视频要上传,你帮我做一个上传后台,下班前给我哦,...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作