首页 > 其他分享 >前端大文件上传

前端大文件上传

时间:2024-05-24 09:30:07浏览次数:21  
标签:文件 重试 index 前端 切片 doneList 上传 chunk

首先,我们需要使用Blob对象的 slice 方法将文件切分成多个切片。

const CHUNK_SIZE = 1024 * 1024; // 我们选择1MB作为每个切片的大小
let file = document.getElementById("upload").files[0]; // 得到所选文件
let totalSize = file.size;
let chunks = []; // 用于存储文件切片的数组
let start = 0; // 初始切片位置

while (start < totalSize) {
  chunks.push(file.slice(start, start + CHUNK_SIZE));
  start += CHUNK_SIZE; // 移动切片的开始位置
}

(2) 文件合并:

有了文件切片后,我们需要在服务器端合并这些切片以重建文件。这通常在服务端完成,前端需要发送一个带有文件唯一标识的请求通知服务器所有切片已发送完毕,可以进行合并。

let fileName = file.name;
let fileId = fileName + '-' + totalSize; // 这是文件的唯一标识,也可使用其他方式
fetch('serverURL/merge', {
  method: 'POST',
    body: JSON.stringify({fileName, fileId}),
  headers: new Headers({'Content-Type': 'application/json'})
});

当我们在进行大文件切片上传时,可以通过在每个分片中包含一个切片的索引来保持切片的顺序。在接收分片的服务器端,可以根据这个索引来为每个分片分配合适的位置,这样即使上传的请求顺序发生了变化,服务器端仍然可以按照原始文件的顺序来组合这些分片。

在前端,你可以像下面这样为每个分片添加一个索引:

chunks.forEach((chunk, index) => {
  let chunkForm = new FormData();
  chunkForm.append('chunk', chunk);
  chunkForm.append('index', index); // 这里的 'index' 就是切片的索引 
});

在服务器端,你可以根据传入的 ‘index’ 来重新组合文件切片,即使分片以无序的方式接收,你仍然可以使用 ‘index’ 参数来保持它们的正确顺序,确保最后合并出来的文件与原文件一致。

例如,如果你在服务器端使用的是Node.js,可能会有类似下面的代码:

// 一个假设的处理文件分片上传的路由处理器
app.post('/upload', (req, res) => {
  let index = req.body.index; // 获得切片的索引
  // ...接下来,使用 'index' 来正确放置文件切片...
});

因此,通过为每个文件切片附加一个索引,我们可以确保在服务器端正确地重新组合这些切片,无论客户端以何种顺序发送这些切片。

(3) 断点续传:

要实现断点续传,需要在每次上传切片完成后在本地存储信息。在上传前检查本地是否有相关记录,若有,则跳过该切片,只上传未完成的部分。

let doneList= JSON.parse(localStorage.getItem(fileId)) || [];

chunks.forEach((chunk, index) => {
  if(!doneList.includes(index)){  // 如果列表中没有本切片的索引,表示该切片未上传成功过
    let chunkForm = new FormData(); // 使用 FormData的方式发送切片
    chunkForm.append('chunk', chunk);
    chunkForm.append('index', index);
    chunkForm.append('fileId', fileId);
    fetch('serverURL/upload', {
   method: 'POST',
   body: chunkForm
  }).then(res => {
      doneList.push(index); // 上传完成后,将切片的索引放入doneList,并且存到localStorage
      localStorage.setItem(fileId, JSON.stringify(doneList));
    });
  }  
});

切片传输失败

判断文件切片是否上传成功的方式通常是通过服务器的响应来实现。在前端发送分片数据到服务器后,服务器会进行处理,如果分片数据被成功接收和存储,服务器通常会返回一个包含状态信息的响应。

在JavaScript中,你可以使用fetch API的.then()方法来处理服务器的响应:

chunks.forEach((chunk, index) => {
fetch(‘serverURL/upload’, {
method: ‘POST’,
body: chunkForm
}).then(response => {
if(response.ok) {
console.log('切片 ‘+ index + ’ 上传成功!’);
doneList.push(index); // 将成功上传的切片索引加入到doneList中
localStorage.setItem(fileId, JSON.stringify(doneList));
} else {
console.log('切片 ‘+ index + ’ 上传失败!’);
}
}).catch(error => {
console.log(‘网络错误:’, error);
})
});

在这个例子中,如果服务器成功处理了上传的切片,会返回一个状态为200的响应,这时,response.ok将为true,我们可以将该切片的索引加入到保存已上传切片的数组中。如果上传失败,或者网络出现错误,我们可以据此进行重试或者报告错误信息。

重试策略

1.处理上传失败的分片并重新上传的最常见策略是使用重试策略。我们可以设置一个最大重试次数,如果上传操作失败,我们会重新尝试上传分片,直到成功或达到最大重试次数。另一个常见策略是增加一个延时,在每次失败后等待一段时间再进行重试,这可以防止短时间内的过多无效尝试。

const MAX_RETRIES = 3; // 最大尝试次数

function uploadChunk(chunk, index, retries = 0) {
  if (retries >= MAX_RETRIES) {
    console.log('切片 '+ index + ' 上传失败!');
    return;
  }

  fetch('serverURL/upload', {
    method: 'POST',
    body: chunkForm
  }).then(response => {
    if (!response.ok) throw new Error('Upload failed');
    console.log('切片 '+ index + ' 上传成功!');
    doneList.push(index); // 将成功上传的切片索引加入到doneList中
    localStorage.setItem(fileId, JSON.stringify(doneList));
  }).catch(error => {
    console.log('出现错误,准备重试:', error);
    setTimeout(() => uploadChunk(chunk, index, retries + 1), 1000); // 如果失败,等待1秒后重试
  });
}

chunks.forEach((chunk, index) => {
  uploadChunk(chunk, index);
});

这个示例首先限制了最大尝试次数,如果达到这个次数,将停止尝试并记录失败信息。如果接收到分片的上传失败的信息,它将等待1秒钟然后重新尝试上传。这种方式可以有效地解决由暂时的网络问题引起的上传失败。

  1. 指数退避:
    指数退避(Exponential Backoff)是一种普遍用于优化网络通信的算法,这种算法通过控制网络请求之间的间隔时间来减少网络拥堵,特别是在由于网络错误而重试请求的场景中。

若要在切片上传失败时应用指数退避策略,你可以使用以下代码:

const MAX_RETRIES = 5; // 最大尝试次数
const RETRY_DELAY = 1000; // 1秒,基准等待时间

function uploadChunk(chunk, index, retries = 0) {
  if (retries >= MAX_RETRIES) {
    console.log("切片 "+ index + " 上传失败!");
    return;
  }

  fetch('serverURL/upload', {
    method: "POST",
    body: chunkForm           // 假设 chunkForm 是你的 FormData 对象
  }).then(response => {
    if (!response.ok) throw new Error("Upload failed");
    console.log('切片 '+ index + ' 上传成功!');
    doneList.push(index);     // 将成功上传的切片索引加入到doneList中
    localStorage.setItem(fileId, JSON.stringify(doneList));
  }).catch(error => {
    console.log("上传失败,准备重试", error);
    // 如果上传切片失败,等待 RETRY_DELAY * (2 ** retries) 毫秒后再次尝试上传
    setTimeout(() => uploadChunk(chunk, index, retries + 1), RETRY_DELAY * (2 ** retries));
  });
}

chunks.forEach((chunk, index) => {
  uploadChunk(chunk, index);
});

在这个例子中,RETRY_DELAY是重试之间的裸等待时间,并且每次失败后会增加重试的等待时间,等待时间计算方式为 RETRY_DELAY * (2 ** retries),即每次重试都将等待时间翻倍。这样可以给网络和服务器更多的恢复时间。

当达到MAX_RETRIES最大尝试次数后,就会停止重试并记录上传失败。

  1. 斐波那契退避:斐波那契退避是一种类似于指数退避的策略,不过它使用的是斐波那契数列而不是指数函数来计算下一次重试之前的暂停时间。

  2. 随机化间隔:为了防止多个客户端同时发起重试请求并再次使服务器过载(这种情况有时被称为“重试风暴”),一种策略是在每次重试之间添加一个随机化的等待时间。

  3. 使用重试库:有许多开源库可以使实施复杂的重试策略变得更容易。例如,在JavaScript中,你可以使用async-retry库来实现带有指数退避的重试。

以上就是文章全部内容了,如果喜欢这篇文章的话,还希望三连支持一下,感谢!

标签:文件,重试,index,前端,切片,doneList,上传,chunk
From: https://blog.csdn.net/weixin_43891869/article/details/139134741

相关文章

  • 微前端qiankun基础环境搭建
    微前端用通俗易懂的话来说就是:一个主应用(基座)中可以搭建多个子应用(微应用),这些子应用可以是不同版本,不同前端框架,而且跟主应用的语言无关,主应用仅仅是一个基座。正常一个项目想要展示另一个项目,通常会用iframe进行嵌入,但是相比iframe,qiankun等微前端的接入表现形式会更加友好,......
  • 前端实现导入,导出/纯前端实现将数据导出为excel,将excel数据导入传给后端
    一、下载插件(xlsx如果报错utils未定义就用下面这个版本)[email protected]@2.0.5二、页面中引入import*asXLSXfrom'xlsx';import{saveAs}from'file-saver'; 三、导入/导出表格内容1.导出exportExcel(){  ......
  • Outlook大文件传送慢、附件太大无法上传,这个办法可以解决!
    Outlook邮箱应用广泛,多个行业多家企业都选择Outlook邮箱作为自己得官方使用邮箱。主要在于Outlook有多个优势:整合性强:Outlook邮箱是MicrosoftOffice套件的一部分,与Word、Excel、PowerPoint等应用程序无缝集成。这使得用户能够轻松地在邮件中创建、编辑和共享文档,提高了工作效率......
  • 文件外发审核是数据防泄漏的重要手段,那该怎么落地?
    企业在日常经营中,无可避免地会产生文件外发的需求,文件发送对象包括但不限于合作方、供应商、客户、公关媒体、慈善组织等等,不一而足。而由于外发的对象不同,所涉及的文件类型也多种多样:商业合作合同:这是双方合作的基础文件,详细规定了合作的范围、条款、责任、权利、付款方式、交货......
  • Web前端-综合网站设计
    Web前端-综合网站设计一、综合网站整体描述综合网站主要包括五个页面,主页、列表页、详情页、购物车页、注册页1.主页:二级菜单、轮播图、Tab显示、克隆、电梯导航2.列表页:Tab显示、手风琴效果3.详细页:数量加减、金额同步、评论发布与删除4.购物车:数量加减、小计同步、总计......
  • 文件的读写
    文件操作:1.打开文件2.读/写-----操作文件test.c------写(输出)------->文件test.c<------读(输入)--------文件文件名:文件路径+文件名主干+文件后缀文件指针:FILE*pf;//文件指针变量//打开文件FILE*fopen(constchar*filename,constchar*mode);//关闭文件intfclose......
  • 基于STM32的环境检测温湿度大气参数上传阿里云的论文
    基于STM32的环境检测温湿度大气参数上传阿里云的论文可以从多个方面来展开,以下是一个论文的概要结构及其内容的建议:一、引言介绍环境检测在现代化生活中的重要性和应用场景。阐述STM32微控制器在环境检测系统中的优势。简述阿里云物联网平台的特点和其在环境检测数据管理中的......
  • 苹果应用上传AppStore
    将ipa提交到AppStore需要Mac电脑操作,现在大部分的程序员都是使用混合开发平台windows系统的电脑,自己装虚拟机过程又繁琐。使用此工具只需要网页上点两下帮你完成这些鸡毛蒜皮事,让你有更多的时间花在改bug上。1.打开苹果应用商店管理后台获取密钥,地址https://appstoreconnect.a......
  • Bash反弹shell & 搭建网页服务器 & 文件描述符学习 & ssh连接vm虚拟机 & sftp进行文件
    环境:kali:┌──(kali㉿kali)-[~/Desktop]└─$cat/proc/versionLinuxversion6.0.0-kali5-amd64([email protected])(gcc-12(Debian12.2.0-9)12.2.0,GNUld(GNUBinutilsforDebian)1.建立一个简单的链接进行nc,可以进行两个端口通信!#首先使用nc监听......
  • nodeJS文件操作
    const{log}=require("console");constfs=require("fs");constpath=require("path");constfilename=path.resolve(__dirname,"./myfiles1.txt");//console.log(filename)//fs.readFile(filename,(err,content)......