大文件上传如何做断点续传

news2024/11/13 11:27:02

大文件上传如何做断点续传

一、是什么

不管怎样简单的需求,在量级达到一定层次时,都会变得异常复杂

文件上传简单,文件变大就复杂

上传大文件时,以下几个变量会影响我们的用户体验

  • 服务器处理数据的能力
  • 请求超时
  • 网络波动

上传时间会变长,高频次文件上传失败,失败后又需要重新上传等等

为了解决上述问题,我们需要对大文件上传单独处理

这里涉及到分片上传及断点续传两个概念

分片上传

分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(Part)来进行分片上传

如下图

上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件

大致流程如下:

  1. 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
  2. 初始化一个分片上传任务,返回本次分片上传唯一标识;
  3. 按照一定的策略(串行或并行)发送各个分片数据块;
  4. 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件

断点续传

断点续传指的是在下载或上传时,将下载或上传任务人为的划分为几个部分

每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度

一般实现方式有两种:

  • 服务器端返回,告知从哪开始
  • 浏览器端自行处理

上传过程中将文件在服务器写为临时文件,等全部写完了(文件上传完),将此临时文件重命名为正式文件即可

如果中途上传中断过,下次上传的时候根据当前临时文件大小,作为在客户端读取文件的偏移量,从此位置继续读取文件数据块,上传到服务器从此偏移量继续写入文件即可

二、实现思路

整体思路比较简单,拿到文件,保存文件唯一性标识,切割文件,分段上传,每次上传一段,根据唯一性标识判断文件上传进度,直到文件的全部片段上传完毕

下面的内容都是伪代码

读取文件内容:

const input = document.querySelector('input');
input.addEventListener('change', function() {
    var file = this.files[0];
});

可以使用md5实现文件的唯一性

const md5code = md5(file);

然后开始对文件进行分割

var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.addEventListener("load", function(e) {
    //每10M切割一段,这里只做一个切割演示,实际切割需要循环切割,
    var slice = e.target.result.slice(0, 10*1024*1024);
});

h5上传一个(一片)

const formdata = new FormData();
formdata.append('0', slice);
//这里是有一个坑的,部分设备无法获取文件名称,和文件类型,这个在最后给出解决方案
formdata.append('filename', file.filename);
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
    //xhr.responseText
});
xhr.open('POST', '');
xhr.send(formdata);
xhr.addEventListener('progress', updateProgress);
xhr.upload.addEventListener('progress', updateProgress);

function updateProgress(event) {
    if (event.lengthComputable) {
        //进度条
    }
}

这里给出常见的图片和视频的文件类型判断

function checkFileType(type, file, back) {
/**
* type png jpg mp4 ...
* file input.change=> this.files[0]
* back callback(boolean)
*/
    var args = arguments;
    if (args.length != 3) {
        back(0);
    }
    var type = args[0]; // type = '(png|jpg)' , 'png'
    var file = args[1];
    var back = typeof args[2] == 'function' ? args[2] : function() {};
    if (file.type == '') {
        // 如果系统无法获取文件类型,则读取二进制流,对二进制进行解析文件类型
        var imgType = [
            'ff d8 ff', //jpg
            '89 50 4e', //png

            '0 0 0 14 66 74 79 70 69 73 6F 6D', //mp4
            '0 0 0 18 66 74 79 70 33 67 70 35', //mp4
            '0 0 0 0 66 74 79 70 33 67 70 35', //mp4
            '0 0 0 0 66 74 79 70 4D 53 4E 56', //mp4
            '0 0 0 0 66 74 79 70 69 73 6F 6D', //mp4

            '0 0 0 18 66 74 79 70 6D 70 34 32', //m4v
            '0 0 0 0 66 74 79 70 6D 70 34 32', //m4v

            '0 0 0 14 66 74 79 70 71 74 20 20', //mov
            '0 0 0 0 66 74 79 70 71 74 20 20', //mov
            '0 0 0 0 6D 6F 6F 76', //mov

            '4F 67 67 53 0 02', //ogg
            '1A 45 DF A3', //ogg

            '52 49 46 46 x x x x 41 56 49 20', //avi (RIFF fileSize fileType LIST)(52 49 46 46,DC 6C 57 09,41 56 49 20,4C 49 53 54)
        ];
        var typeName = [
            'jpg',
            'png',
            'mp4',
            'mp4',
            'mp4',
            'mp4',
            'mp4',
            'm4v',
            'm4v',
            'mov',
            'mov',
            'mov',
            'ogg',
            'ogg',
            'avi',
        ];
        var sliceSize = /png|jpg|jpeg/.test(type) ? 3 : 12;
        var reader = new FileReader();
        reader.readAsArrayBuffer(file);
        reader.addEventListener("load", function(e) {
            var slice = e.target.result.slice(0, sliceSize);
            reader = null;
            if (slice && slice.byteLength == sliceSize) {
                var view = new Uint8Array(slice);
                var arr = [];
                view.forEach(function(v) {
                    arr.push(v.toString(16));
                });
                view = null;
                var idx = arr.join(' ').indexOf(imgType);
                if (idx > -1) {
                    back(typeName[idx]);
                } else {
                    arr = arr.map(function(v) {
                        if (i > 3 && i < 8) {
                            return 'x';
                        }
                        return v;
                    });
                    var idx = arr.join(' ').indexOf(imgType);
                    if (idx > -1) {
                        back(typeName[idx]);
                    } else {
                        back(false);
                    }

                }
            } else {
                back(false);
            }

        });
    } else {
        var type = file.name.match(/\.(\w+)$/)[1];
        back(type);
    }
}

调用方法如下

checkFileType('(mov|mp4|avi)',file,function(fileType){
    // fileType = mp4,
    // 如果file的类型不在枚举之列,则返回false
});

上面上传文件的一步,可以改成:

formdata.append('filename', md5code+'.'+fileType);

有了切割上传后,也就有了文件唯一标识信息,断点续传变成了后台的一个小小的逻辑判断

后端主要做的内容为:根据前端传给后台的md5值,到服务器磁盘查找是否有之前未完成的文件合并信息(也就是未完成的半成品文件切片),取到之后根据上传切片的数量,返回数据告诉前端开始从第几节上传

如果想要暂停切片的上传,可以使用XMLHttpRequest abort 方法

三、使用场景

  • 大文件加速上传:当文件大小超过预期大小时,使用分片上传可实现并行上传多个 Part, 以加快上传速度
  • 网络环境较差:建议使用分片上传。当出现上传失败的时候,仅需重传失败的Part
  • 流式上传:可以在需要上传的文件大小还不确定的情况下开始上传。这种场景在视频监控等行业应用中比较常见

小结

当前的伪代码,只是提供一个简单的思路,想要把事情做到极致,我们还需要考虑到更多场景,比如

  • 切片上传失败怎么办
  • 上传过程中刷新页面怎么办
  • 如何进行并行上传
  • 切片什么时候按数量切,什么时候按大小切
  • 如何结合 Web Worker 处理大文件上传
  • 如何实现秒传

人生又何尝不是如此,极致的人生体验有无限可能,越是后面才发现越是精彩 _

参考文献

  • https://segmentfault.com/a/1190000009448892
  • https://baike.baidu.com/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/134823.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信息安全3——数字签名和认证

1 &#xff09;签名&#xff1a;手写签名是被签文件的物理组成部分&#xff0c;而数字签名不是被签消息的物理部分&#xff0c;因而需要将签名连接到被签消息上。 2 &#xff09;验证&#xff1a;手写签名是通过将它与其它真实的签名进行比较来验证而数字签名是利用已经公开的验…

年终总结(我心飞翔向)

2022 年度个人总结&#xff08;自由向&#xff09; 前奏 其实在2021年12月底考研前就回家了&#xff0c;回家做毕设。他们考研的那几天回了中北&#xff0c;参加了党支部会议&#xff0c;见证了一批同学的转预转正&#xff1b;收拾了一大波衣服&#xff0c;因为我已经提前想到…

Git(三) - Git 常用命令

一、设置用户签名 说明&#xff1a; 签名的作用是区分不团操作者身份。用户的签名信息在每一个版本的提交信息中能够看到&#xff0c;以此确认本次提交是谁做的。GIT 首次安装必须设置一下用户签名&#xff0c;否则无法提交代码。 注意&#xff1a; 这里设置用户前面和将来登录…

微机原理真题2019年,错题整理

目录 2019年 填空 编程 1​编辑 2 3 练习册的题 2019年 1&#xff1a;在计算机中能够在一组信息中取出所需要的一部分信息的器件是&#xff08;&#xff09; A:触发器 B:寄存器 C:译码器 D:锁存器 2&#xff1a;宏汇编程序中一般由3个段组成&#xff0c;这三…

FreeRTOS实验使用01

1&#xff1a;vTaskList的使用 我使用的时候&#xff0c;如果把pcWriteBuff定义在任务中&#xff0c;程序会卡死&#xff0c;不信你可以尝试一下&#xff0c;所以我就把pcWriteBuff定义到了全局中&#xff0c;才能使用 2&#xff1a;队列问题 场景&#xff1a;创建3个格子的队…

搜狗 workflow异步调度框架(二)HTTP客户端

1.避免进程提前终止 由于任务的启动是异步的&#xff0c;所以任务的执行和主线程的执行是并行的&#xff0c;如果不加任何的控制&#xff0c;那么当主线程执行完所有操作以后直接退出&#xff0c;并且导致整个进程的终止。 WFFacilities::WaitGroup 可以根据情况阻塞线程或者恢…

DDR3 数据传输 (六)

引言 前文链接: DDR3 数据传输 (一) DDR3 数据传输 (二) DDR3 数据传输 (三) DDR3 数据传输 (四) DDR3 数据传输 (五) 本文在前文设计的基础上,给出板级验证。<

Spring Boot MongoDB 入门

1. 概述 2. 快速入门 3. 基于方法名查询 4. 基于 Example 查询 5. MongoTemplate 6. 自增主键 666. 彩蛋 1. 概述 可能有一些胖友对 MongoDB 不是很了解&#xff0c;这里我们引用一段介绍&#xff1a; FROM 《分布式文档存储数据库 MongoDB》 MongoDB 是一个介于关系数据…

《计算机视觉》:角点检测与图像匹配

文章目录 任务一:基本处理-Harris角点检测原理代码结果与分析任务二:SIFT算法原理代码结果与分析任务一:基本处理-Harris角点检测 数据:棋盘图片 要求:自己写函数实现Harris角点检测子,设置不同参数,比较检测结果 边缘检测子:sobel检测子 响应函数参数alpha:0.05 参数…

【JavaScript】BOM 概念及相关操作

文章目录【JavaScript】BOM 概念及相关操作一. BOM概念BOM可以操作的内容二.window内置对象和属性(1) 获取浏览器窗口的尺寸(2) 获取文档窗口的尺寸(3) 浏览器的常见事件(4) 浏览器的历史记录(5) 浏览器的标签页(6) 浏览器卷去的尺寸(7) 浏览器滚动到的位置浏览器滚动到的位置案…

ARM 按键轮询编程实战

一、什么是按键 1、按键的物理特性 平时没人按的时候&#xff0c;弹簧把按键按钮弹开。此时内部断开的。有人按下的时候&#xff0c;手的力量克服弹簧的弹力&#xff0c;将按钮按下&#xff0c;此时内部保持接通&#xff08;闭合&#xff09;状态&#xff1b;如果手拿开&…

【应急响应】 - Windows 排查分析

Windows 分析排查1. 文件分析1.1 开机启动文件1.2 temp 临时异常文件1.3 浏览器信息分析1.4 文件时间属性分析1.5 最近打开文件分析2. 进程分析2.1 可疑进程发现与关闭3. 系统信息3.1 windows 计划任务3.2 隐藏账户与发现3.2.1 隐藏账号的建立3.2.2 隐藏账号的删除3.3 补丁查看…

Java开发的党员管理系统党员会议系统党务管理系统

简介 Java开发的大学生党员管理系统&#xff0c;主要功能会议&#xff0c;会议记录&#xff0c;会议主持&#xff0c;设置参会人员&#xff0c;请假申请&#xff0c;会议内容附件上传下载&#xff0c;党费管理&#xff0c;入党积极分子预备党员管理&#xff0c;人员变动&#…

hcip实验

1.搭建拓扑 2.配置IP R14&#xff1a; [r14]ip route-static 0.0.0.0 0 145.1.1.2 [r14]acl 2000 [r14-acl-basic-2000]rule permit source any [r14]int GigabitEthernet 0/0/1 [r14-GigabitEthernet0/0/1]nat outbound 2000 [r14]int Tunnel 0/0/0 [r14-Tunnel0/0/0…

【2 - 随机森林 - 原理部分】菜菜sklearn机器学习

课程地址&#xff1a;《菜菜的机器学习sklearn课堂》_哔哩哔哩_bilibili 第一期&#xff1a;sklearn入门 & 决策树在sklearn中的实现第二期&#xff1a;随机森林在sklearn中的实现第三期&#xff1a;sklearn中的数据预处理和特征工程第四期&#xff1a;sklearn中的降维算法…

DDR3 数据传输 (四)

目录 引言 AXI从侧接口参数 AXI从侧接口信号 参考说明 引言 前文链接

【数学思维】数理经济中一些基本概念

【数学思维】数理经济中一些基本概念开集 open set 与闭集 closed set紧集 compact set集合有界 bounded set度量空间 metric space欧式空间 euclidean space闭包 closure上包络 upper envelope、下包络 lower envelope上极限 limit superior、下极限 limit inferior左连续、右…

RabbitMQ第五个实操小案例——主题交换机(TopicExchange)

文章目录RabbitMQ第五个实操小案例——主题交换机&#xff08;TopicExchange&#xff09;RabbitMQ第五个实操小案例——主题交换机&#xff08;TopicExchange&#xff09; TopicExchange 和 DirectExchange 这两种交换机非常相似&#xff0c;Topic类型的Exchange与Direct相比&…

JavaScript 面向对象的编程 (Code with mosh学习笔记)

JavaScript OOP Getting Start - 1- What is OOP 面向对象的编程是一种编程范例围绕对象而不是函数一些OOP语言 C#JavaRubyPythonJavaScript Getting Start - 2- Four Pillars of OOP OOP的4个概念&#xff1a; 封装 使用封装重新组合相关的变量和函数减少复杂性增加代码…

jrtt 某头条网页版 _signature参数逆向

本文仅供参考学习&#xff0c;如有侵权可联系本人 目标网站 aHR0cHM6Ly93d3cudG91dGlhby5jb20vYy91c2VyL3Rva2VuL01TNHdMakFCQUFBQWE0alpUdzhvRlZnaUJIREprMTA1NDdBVFBUb050aHlsVDRqWndZMmlrMXcvPw接口分析 token&#xff1a;需要采集用户 _signature&#xff1a;加密参数 ai…