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

news2024/9/28 9:23:06

一、是什么

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

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

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

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

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

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

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

分片上传

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

如下图

 

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

大致流程如下:

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

断点续传

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

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

一般实现方式有两种:

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

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

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

二、实现思路

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

 

下面的内容都是伪代码

读取文件内容:

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 Work 处理大文件上传
  • 如何实现秒传

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

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

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

相关文章

缓存穿透-总结

目录 缓存穿透-总结 出现场景&#xff1a; 解决方法&#xff1a; 方法1.缓存空对象&#xff1a; 方法2.加一个布隆过滤器&#xff1a; 总结&#xff1a; 缓存穿透-总结 出现场景&#xff1a; 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓…

光量子领域新突破:有望打造芯片工厂!

将2D材料与氮化硅谐振器混合集成&#xff0c;使一系列单光子源与硅基光子按需精准结合。&#xff08;图片来源&#xff1a;网络&#xff09;量子光子学的著名专家、电气和计算机工程助理教授Galan Moody的实验室成功创造了一种在芯片上产生单光子的新方法。量子具有叠加态的特性…

飞桨特色产业级模型库,助力AI开发与落地更简单!

飞桨在长期的产业实践中发现&#xff0c;开发者使用开源模型项目落地普遍会遇到三大难题&#xff1a; 算法和模型繁多&#xff0c;做模型选择是个难题&#xff1b; 模型效果不错&#xff0c;但产业落地时容易遇到资源限制和部署的问题&#xff1b; 面对新场景无从下手&#x…

minio public桶禁止在直接访问桶位置时列出所有文件url

minio的public桶因为没有限制&#xff0c;所以在直接访问到桶地址的时候会列出桶内所有文件的url&#xff0c;这样很不安全&#xff0c;如何禁止这个功能&#xff0c;可以使用三种方法 1、如果是新版的可以直接设置桶的Access Policy为自定义就好 编辑custom的Policy&#xff…

五种情况下企业需要引进低代码开发平台

随着低代码开发平台的热度在上升&#xff0c;企业中也开始流行一种新的应用交付方式&#xff1a;业务部门基于低代码开发平台将所需要的功能&#xff08;或简单的可用版本&#xff09;自行搭建出来&#xff0c;当遇到较为复杂的需求时&#xff0c;则向IT部门请求支援。业务与IT…

【MFC】模拟采集系统——数据绘制(19)

完成界面设计后&#xff0c;数据绘制也可以按照对MFC类派生来完成&#xff0c;值得注意的是这里的数据绘制仅仅是通过随机产生的数据来显示&#xff0c;并且显示的方法也有很多。 数据绘制 在主对话框中添加两个 Picture Control 位置大小任意&#xff0c;可以设置一下外观&a…

Python3 数据结构实例及演示

本章节主要结合前面所学的知识点来介绍Python数据结构。 列表 Python中列表是可变的&#xff0c;这是它区别于字符串和元组的最重要的特点&#xff0c;1句话概括即&#xff1a;列表可以修改&#xff0c;而字符串和元组不能。 以下是 Python 中列表的方法&#xff1a; 下面示…

阿里云服务器部署前后端分离项目

阿里云服务器部署 【若依】 前后端分离项目 文章目录一、域名解析二、服务器操作系统置空三、部署方式四、需安装环境配置五、Linux服务器安装相应内容&#xff08;具体安装步骤&#xff09;&#xff08;一&#xff09;安装JDK&#xff08;3种方式&#xff09;使用Yum安装&…

Assignment写作各个部分怎么衔接完美?

Assignment格式很简单&#xff0c;就只有四个部分&#xff0c;按着通用的套路来&#xff0c;发现也没什么难度。不过这4个部分自己需要衔接完美&#xff0c;下面就给大家分享一下写Assignment最简单的方法。 如果没有目录可以放在第一页的开头&#xff0c;用“标题字体”加重显…

互联网大厂Java岗最全八股文面试1100道真题汇总,堪称2023年面试天花板

2023 年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;一直以来我都想整理一套完美的面试宝典&#xff0c;奈何难抽出时间&#xff0c;这套 1100道的 Java 面试手册是行业内各大神联合总结出来的&#xff0c;上传到 Git 上目前 star 数达到了 30K 这套互…

POE指数积公式的简单matlab实现

参考现代机器人学 及机器人工程师进阶之路&#xff08;八&#xff09;指数积&#xff08;PoE&#xff09;建立机械臂模型及正运动 1.使用Robotics在matlab中建立一个简单的机械臂&#xff0c;方便验证后面的代码写的对不对 %Robotics 工具箱介绍&#xff1a; https://blog.csd…

【使用secureCRT管理Linux系统】

使用secureCRT管理Linux系统 进入secureCRT快速连接虚拟机 默认是SSH2类型&#xff0c;红线是虚拟机的ip地址&#xff0c;绿线是虚拟机的用户名称 如果之后的页面出现了需要输入用户密码&#xff0c;那就是成功了&#xff0c;如果出现了Connection timed out 可能原因是电脑…

Spring如何整合MyBatis框架?使用XML及java类的配置方式

前言 Spring文章链接: 从头到尾Spring概念&#xff0c;家族&#xff0c;核心技术使用&#xff0c;事务这一篇就够了&#xff01;&#xff01;&#xff01;_千小半的博客-CSDN博客_spring最新技术 mybatis文章链接: MyBatis框架入门(含实例)_mybatis sqlsession创建和关闭_千小…

文华财经期货日内多空金叉波段彩带幅图指标公式,震荡区间决策预警信号指标公式

​期货指标公式不是交易的圣杯&#xff0c;也不是期货亏损后的救命稻草。请理性运用指标公式&#xff0c;独立决策&#xff0c;盈亏自负。 1.在期货交易中&#xff0c;大部分品种&#xff0c;大部分时间都是在走一个趋势行情。即使是形态内的震荡行情&#xff0c;也是在走一个…

redis 实现延迟队列及其他实现延迟队列

1、 前言 1.1、什么是延迟队列&#xff1f; 延时队列相比于普通队列最大的区别就体现在其延时的属性上&#xff0c;普通队列的元素是先进先出&#xff0c;按入队顺序进行处理&#xff0c;而延时队列中的元素在入队时会指定一个延迟时间&#xff0c;表示其希望能够在经过该指定…

虹科新品|HK-Edgility敏捷边缘计算套件

敏捷、经济高效且易于管理大规模IT服务基础设施 业务动态和数字化转型将业务资源和业务用户都推到了传统 IT 边界之外。业务资源和服务被虚拟化并迁移到云端。随着数字工作空间的发展和业务用户需要随时随地访问业务资源&#xff0c;越来越多的业务资源被推向网络边缘。 由于…

研报精选230216

目录 【行业230216东吴证券】环保行业月报&#xff1a;2023M1环卫新能源渗透率大增至11.91%&#xff0c;上海地区渗透率高达77%【行业230216国元证券】国元新食饮&#xff1a;一图君&#xff1a;22年白酒产量&#xff1a;同降6.2%【行业230216浙商证券】农林牧渔点评报告&#…

认识数据库

今天为大家带来数据库的知识 &#x1f680;1.什么是数据库 &#x1f680;2.数据库的分类 &#x1f680;3.数据库的存储 1.首先来聊一聊MySQL数据库的定义 已经学习了数据结构 数据结构是一门研究数据如何存储的学科&#xff0c;当数据是少量的时候&#xff0c;用数据结构…

在vue2使用百度脑图的kityminder-core进行二次开发思维导图,给节点绑定数据后添加新的图标

需求说明&#xff1a;在给某个节点绑定文件数据后&#xff0c;用户并不能一眼看出哪个节点上绑定了数据&#xff0c;因此需要在绑定文件数据后给节点上加一个图标用于标识。 添加图标 1、在kityminder-core/src/module/file.js文件中添加代码 &#xff08;file.js文件如何添加…

ChatGPT国内镜像站初体验:聊天、Python代码生成等

ChatGPT国内镜像站试用&#xff0c;聊天、Python代码生成。 (本文获得CSDN质量评分【92】)【学习的细节是欢悦的历程】Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那么简单…… …