react 文件 md5_字节跳动面试题 | 大文件上传和断点续传的几点思考

本文探讨了React中大文件上传和断点续传的实现策略,包括时间切片计算文件MD5避免主线程阻塞、抽样哈希以提高效率、网络请求并发控制、进度条优化、并发重试与错误处理、文件碎片清理等。还分享了面试题中的解决方案和后续扩展思考。
摘要由CSDN通过智能技术生成
9fe60d27b8befdc9a110e1fac7da34ca.gif 本期作者 盛鑫晶(大圣) 开课吧Web专家讲师/前百度和360前端架构师,8年前端开发经验,精通Vue/React、源码架构、小程序、移动端和Nodejs整个前端技术栈,对前端萌新如何快速进阶有丰富的经验,业内少有既精通技术又精通授课的大咖。 前几天看到一个文章,感触很深: 《字节跳动面试官:请你实现一个大文件上传和断点续传》 | 原作者:yeyan1996 | 原链接:https://url.cn/5h66afn 作者从0实现了大文件的切片上传,断点续传,秒传,暂停等功能,深入浅出的把这个面试题进行了全面的剖析。 彩虹屁不多吹,我决定蹭蹭热点,录录视频,把作者完整写代码的过程加进去,并且接着这篇文章写,所以请看完上面的文章后再食用,我做了一些扩展如下: 作者从0实现了大文件的切片上传,断点续传,秒传,暂停等功能,深入浅出的把这个面试题进行了全面的剖析。 彩虹屁不多吹,我决定蹭蹭热点,录录视频,把作者完整写代码的过程加进去,并且接着这篇文章写,所以 请看完上面的文章后再食用 ,我做了一些扩展如下:

「1」计算 hash耗时的问题,不仅可以通过 web-workder,还可以参考 React的 FFiber架构,通过requestIdleCallback来利用浏览器的空闲时间计算,也不会卡死主线程。

「2」文件 hash的计算,是为了判断文件是否存在,进而实现秒传的功能,所以我们可以参考布隆过滤器的理念, 牺牲一点点的识别率来换取时间,比如我们可以抽样算hash

「3」文中通过 web-workder让 hash计算不卡顿主线程,但是大文件由于切片过多,过多的 HTTP链接过去,也会把浏览器打挂 (我试了4个G的,直接卡死了), 我们可以通过控制异步请求的并发数来解决,我记得这也是头条的一个面试题。

「4」每个切片的上传进度不需要用表格来显示,我们换成方块进度条更直观一些(如图)。

「5」并发上传中,报错如何重试,比如每个切片我们允许重试两次,三次再终止。

「6」由于文件大小不一,我们每个切片的大小设置成固定的也有点略显笨拙,我们可以参考TCP协议的慢启动策略, 设置一个初始大小,根据上传任务完成的时候,来动态调整下一个切片的大小, 确保文件切片的大小和当前网速匹配。

「7」小的体验优化,比如上传的时候。

「8」文件碎片清理。

22c8a7a22cd345e8a3a456fdaec67a1c.gif 已经存在的秒传的切片就是绿的,正在上传的是蓝色的,并发量是4,废话不多说,我们一起代码开花。

01 时间切片计算文件hash

其实就是 time-slice 概念, ReactFiber 架构的核心理念,利用浏览器的空闲时间,计算大的diff过程,中途有任何的高优先级任务,比如动画和输入,都会中断diff任务, 虽然整个计算量没有减小,但是大大提高了用户的交互体验。 这可能是最通俗的 React Fiber(时间分片) 打开方式。(https://juejin.im/post/5dadc6045188255a270a0f85)

2ee0c2ac3548f9dda27951c51f300e03.png

window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作 requestIdelCallback执行的方法,会传递一个 deadline参数,能够知道当前帧的剩余时间,用法如下:

requestIdelCallback(myNonEssentialWork);

function myNonEssentialWork (deadline) {

// deadline.timeRemaining()可以获取到当前帧剩余时间

// 当前帧还有时间 并且任务队列不为空

while(deadline.timeRemaining() > 0&& tasks.length > 0) {

doWorkIfNeeded();

}

if(tasks.length > 0){

requestIdleCallback(myNonEssentialWork);

}

}

deadline的结构如下:

interfaceDealine{

didTimeout: boolean// 表示任务执行是否超过约定时间

timeRemaining(): DOMHighResTimeStamp// 任务可供执行的剩余时间

}

739880c982748eddcfc1fdde22649a45.png 该图中的两个帧,在每一帧内部, TASKredering 只花费了一部分时间,并没有占据整个帧,那么这个时候,如图中 idle period 的部分就是空闲时间,而每一帧中的空闲时间,根据该帧中处理事情的多少,复杂度等,消耗不等,所以空闲时间也不等。 而对于每一个 deadline.timeRemaining() 的返回值,就是如图中, IdleCallback 到所在帧结尾的时间(ms级)。 我们接着之前文章的代码,改造一下 calculateHash

async calculateHashIdle(chunks) {

returnnewPromise(resolve => {

const spark = newSparkMD5.ArrayBuffer();

let count = 0;

// 根据文件内容追加计算

const appendToSpark = async file => {

returnnewPromise(resolve => {

const reader = newFileReader();

reader.readAsArrayBuffer(file);

reader.onload = e => {

spark.append(e.target.result);

resolve();

};

});

};

const workLoop = async deadline => {

// 有任务,并且当前帧还没结束

while(count < chunks.length && deadline.timeRemaining() > 1) {

await appendToSpark(chunks[count].file);

count++;

// 没有了 计算完毕

if(count < chunks.length) {

// 计算中

this.hashProgress = Number(

((100* count) / chunks.length).toFixed(2)

);

// console.log(this.hashProgress)

} else{

// 计算完毕

this.hashProgress = 100;

resolve(spark.end());

}

}

window.requestIdleCallback(workLoop);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值