一文搞懂分片上传和断点续传(代码片段)

autofelix autofelix     2022-12-16     556

关键词:

〝 古人学问遗无力,少壮功夫老始成 〞

一文搞懂分片上传和断点续传,对于做过文件上传的小伙伴对于这两个名词并不太陌生,而在上传大文件的业务中,这两种上传方式是经常被用到的,但是很多小白对这两种上传的方法的用法还是有点模糊,如果这篇文章能给你带来一点帮助,希望给飞兔小哥哥一键三连,表示支持,谢谢各位小伙伴们。

目录

一、什么是分片上传、断点续传

二、项目地址

三、分片上传实战

四、如何进行切片

五、后端对分片的处理

六、断点续传 


一、什么是分片上传、断点续传

  •  分片上传:将一个文件切割为一系列特定大小的小数据片,分别将这些小数据片分别上传到服务端,全部上传完后再在服务端将这些小数据片合并成为一个完整的资源。
  • 断点续传:在分片上传的过程中,如果因为系统崩溃或者网络中断等异常因素导致上传中断,这时候客户端需要记录上传的进度。在之后支持再次上传时,可以继续从上次上传中断的地方进行继续上传
  • 在网络不好,文件很大的情况下可以采用分片上传
  • 而因为网络崩溃或者系统中断,可以采用断点续传

二、项目地址

三、分片上传实战

  • 将完整视频切成每一个小片
  • 这里我们使用的是jquery.fcup官方切片库
  • 完整源代码都在上面的项目地址中
  • 前端我们只要引入该切片库,进行以下简单的配置即可
$.fcup(
    upId: 'upid', //上传dom的id
    upShardSize: '1', //切片大小,(单次上传最大值)单位M,默认2M
    upMaxSize: '200', //上传文件大小,单位M,不设置不限制
    upUrl: 'fcup.php', //文件上传接口
    upType: 'jpg,png,jpeg,gif,mp4', //上传类型检测,用,号分割
    //接口返回结果回调,根据结果返回的数据来进行判断,可以返回字符串或者json来进行判断处理
    upCallBack: function (res) 
        var status = res.status;
        var msg = res.msg;
        // 已上传完成
        if (res.status == 2) 
            console.log(msg);
        
        // 还在上传中
        if (status == 1) 
            console.log(msg);
        
        // 接口返回错误
        if (status == 0) 
            // 停止上传并且提示信息
            $.upStop(msg);
        
    ,
    // 上传过程监听,可以根据当前执行的进度值来改变进度条
    upEvent: function (num) 
        // num的值是上传的进度,从1到100
        console.log('当前上传进度:' + num + '%');
    ,
    // 发生错误后的处理
    upStop: function (errmsg) 
        // 这里只是简单的alert一下结果,可以使用其它的弹窗提醒插件
        // alert(errmsg);
    ,
    // 开始上传前的处理和回调,比如进度条初始化等
    upStart: function () 
        alert('开始上传');
    
);

四、如何进行切片

  • 在切片库中我们可以看到以下流程
  • 第一步:加载配置、获取dom元素、执行fcup_addFileInput方法
  • 第二步:其实就是获取上传dom元素的位置然后在该位置加一个透明的文件上传按钮,这样用户在点击dom元素的时候,其实点击的是这个透明的文件上传按钮,弹出上传文件的选项
  • 第三步:第二步其实已经初始化完毕,后续操作需要用户点击,触发fcup_upload上传方法
  • 第四步:进行切片上传核心代码,设置分片大小、切片,递归通过ajax进行上传
//这是第一步
fcup: function (config) 
        jQuery.extend(config);
        if (jQuery.upId && jQuery.upUrl) 
            jQuery.domHtml = jQuery('#' + jQuery.upId).html();
            jQuery.fcup_addFileInput();
        
    ,
//这是第二步
fcup_addFileInput: function () 
        jQuery.upInputId = jQuery.upId + '_input';
        var C = jQuery('#' + jQuery.upId).attr("class");
        var X = jQuery('#' + jQuery.upId).position().top;
        var Y = jQuery('#' + jQuery.upId).position().left;
        var W = jQuery('#' + jQuery.upId).innerWidth();
        var H = jQuery('#' + jQuery.upId).innerHeight();
        var html = jQuery.domHtml;
        if (C) 
            html += '<input type="file" id="' + jQuery.upInputId + '" class="' + C + '" onchange="jQuery.fcup_upload()" style="position:absolute;left:' + Y + 'px;top:' + X + 'px;opacity:0;z-index:9999;width:' + W + 'px;height:' + H + 'px;">';
         else 
            html += '<input type="file" id="' + jQuery.upInputId + '" onchange="jQuery.fcup_upload()" style="position:absolute;left:' + Y + 'px;top:' + X + 'px;opacity:0;z-index:9999;width:' + W + 'px;height:' + H + 'px;">';
        
        jQuery('#' + jQuery.upId).html(html);
    ,
//这是第三步
fcup_upload: function () 
        var result = '';
        jQuery.upError = '';
        var file = jQuery('#' + jQuery.upInputId)[0].files[0];
        if (!file) 
            return false;
        
        if (typeof jQuery.upStart == 'function') 
            jQuery.upStart();
        
        name = file.name;
        size = file.size;
        index1 = name.lastIndexOf(".");
        if (!jQuery.upShardSize) 
            jQuery.upShardSize = 2;
        
        var fileMD5 = jQuery.get_file_md5(file);
        var index2 = name.length,
            suffix = name.substring(index1 + 1, index2),
            shardSize = jQuery.upShardSize * 1024 * 1024,
            succeed = 0,
            shardCount = Math.ceil(size / shardSize);

        if (jQuery.upType) 
            uptype = jQuery.upType.split(",");
            if (jQuery.inArray(suffix, uptype) == -1) 
                jQuery.upError = '不允许上传的文件类型-' + suffix;
            
        
        if (jQuery.upMaxSize) 
            if (!jQuery.fcup_limitFileSize(file, jQuery.upMaxSize + 'MB')) 
                jQuery.upError = '上传文件过大';
            
        
        if (jQuery.upStatus() == false) 
            return false;
        
        setInterval("jQuery.fcup_upFileInput();",500);
        var re = [];
        var start, end = 0;
        for (var i = 0; i < shardCount; ++i) 
            re[i] = [];
            start = i * shardSize,
                end = Math.min(size, start + shardSize);
            re[i]["file_data"] = file.slice(start, end);
            re[i]["file_name"] = name;
            re[i]["file_size"] = size; //文件大小
        
        const URL = jQuery.upUrl;
        var i2 = 0, i3 = 1, fcs = Array();
        var xhr = new XMLHttpRequest();
        function ajaxStack(stack) 
            if (jQuery.upStatus() == false) 
                return;
            
            var form = new FormData();
            if (stack[i2]) 
                fcs = stack[i2];
                form.append("file_data", fcs['file_data']);
                form.append("file_name", fcs['file_name']);
                form.append("file_size", fcs['file_size']);
                form.append("file_md5", fileMD5);
                form.append("file_total", shardCount);
                form.append("file_index", i3);
                xhr.open('POST', URL, true);
                xhr.onload = function () 
                    ajaxStack(stack);
                
                xhr.onreadystatechange = function () 
                    if (jQuery.upStatus() == false) 
                        return;
                    
                    if (xhr.readyState == 4 && xhr.status == 200) 
                        var data = xhr.responseText ? eval('(' + xhr.responseText + ')') : '';
                        ++succeed;
                        var cent = jQuery.fcup_getPercent(succeed, shardCount);
                        if (typeof jQuery.upEvent == 'function') 
                            jQuery.upEvent(cent);
                        
                        if (cent == 100) 
                            setTimeout(function () 
                                if (typeof jQuery.upCallBack == 'function') 

                                    jQuery.upCallBack(data);
                                
                            , 500);
                         else 
                            if (typeof jQuery.upCallBack == 'function') 
                                jQuery.upCallBack(data);
                            
                        
                    
                
                xhr.send(form);
                i2++; i3++;
                form.delete('file_data');
                form.delete('file_name');
                form.delete('file_size');
                form.delete('file_md5');
                form.delete('file_total');
                form.delete('file_index');
            
        
        ajaxStack(re);
        re = null,
            file = null;
    ,
//这是核心代码
for (var i = 0; i < shardCount; ++i) 
            re[i] = [];
            start = i * shardSize,
                end = Math.min(size, start + shardSize);
            re[i]["file_data"] = file.slice(start, end);
            re[i]["file_name"] = name;
            re[i]["file_size"] = size; //文件大小
        

五、后端对分片的处理

  • 因为每个分片的md5值是唯一的
  • 所以可以通过md5值判断分片文件是否已经上传
  • 如果已经上传可以直接跳过,否则进行分片的保存
  • 通过索引和总数进行对比判断是否上传成功
  • 上传成功的话,将分片进行合并成一个完整的文件
  • 因为服务器内存的最大化,文件合并完毕就可以将分片删除清理
private function run($file)
    
        //上传分片到分片目录
        $shard_file = $file['file_md5'] . DIRECTORY_SEPARATOR . $file['file_index'];

        //跳过重复文件
        $result = file_exists($shard_file) ?: move_uploaded_file($file['file_data']['tmp_name'], $shard_file);

        if ($this->isFinish($file) && $result) 
            try 
                for ($i = 0; $i < $file['file_total']; $i++) 
                    $fileIndex = $i + 1;
                    $blob = file_get_contents($file['file_md5'] . DIRECTORY_SEPARATOR . $fileIndex);

                    file_put_contents($this->saveRoot . DIRECTORY_SEPARATOR . $file['file_name'], $blob, FILE_APPEND);

                    //删除分片文件
                    array_map('unlink', glob("$file['file_md5']/$fileIndex"));
                

                //删除分片文件夹
                @rmdir($file['file_md5']);

                $this->jsonBack([
                    'status' => static::UPLOAD_FINISH,
                    'msg' => '上传完成'
                ]);
             catch (Exception $e) 
                $this->jsonBack([
                    'status' => static::UPLOAD_ERROR,
                    'msg' => $e->getMessage()
                ]);
            
         else 
            if ($result) 
                $this->jsonBack([
                    'status' => static::UPLOAD_START,
                    'msg' => '正在上传:' . $file['file_index'] . '/' . $file['file_total']
                ]);
             else 
                $this->jsonBack([
                    'status' => static::UPLOAD_ERROR,
                    'msg' => '上传错误,请重新上传'
                ]);
            
        
    

六、断点续传 

  • 其实在上面流程已经可以显示出来了
  • 上传的时候有个索引值,告诉你,当前已经传到哪一个分片了
  • 如果系统崩溃,导致上传断开,可以判断这个索引值,上传过的可以略过,进行后续的上传

前端+后端实现分片上传(断点续传/极速秒传)(代码片段)

...大文件/视频前后端(java)代码前端+后端实现分片上传(断点续传/极速秒传)前端slice分片上传,后端用表记录分片索引和分片大小和分片总数,当接受完最后一个分片(分片索引等于分片总数࿰... 查看详情

springboot实现分片上传断点续传大文件极速秒传-备忘(代码片段)

...传体验呢,答案有的,就是下边要介绍的几种上传方式1.分片上传1.1什么是分片上传  分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服... 查看详情

项目难点——断点续传分片上传(代码片段)

项目难点——【2】断点续传、分片上传1文件分片在网络请求中,如果我们有时是上传大文件,可能由于网络原因,导致上传断断续续,很难一次性上传成功,那么这个时候我们就需要将大文件分块,分成... 查看详情

ios大文件分片上传和断点续传

总结一下大文件分片上传和断点续传的问题。因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况。http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件切片(分块)... 查看详情

超大文件上传和断点续传的源代码

总结一下大文件分片上传和断点续传的问题。因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况。http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件切片(分块)... 查看详情

实战:5分钟搞懂okhttp断点上传(代码片段)

1、前言经常会有同学问:文件的断点上传如何实现?断点上传/下载,这是在客户端经常遇到的场景,当我们需要上传或下载一个大文件时,都会考虑使用断点续传的方式。断点上传相较于断点下载来说,... 查看详情

超大文件上传和断点续传的源代码

总结一下大文件分片上传和断点续传的问题。因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况。http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件切片(分块)... 查看详情

springboot分片上传断点续传大文件极速秒传功能,这篇都帮你搞定!(典藏版)...(代码片段)

...呢,答案有的,就是下边要介绍的几种上传方式1.分片上传1.1什么是分片上传分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,... 查看详情

jsp利用webuploader实现超大文件分片上传断点续传

​需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。第一步:前端修改由于项目使用的是BJUI前端框架,并... 查看详情

阿里云oss文件上传(分片上传断点续传)前后端实现(代码片段)

转载地址:https://segmentfault.com/a/1190000020963346         关于阿里云OSS的介绍请参考官方文档:阿里云OSS。出于账号安全的考虑,前端使用OSS服务需要走临时授权,即拿一个临时凭证(STSToken)去调用aliyun... 查看详情

阿里云oss文件上传(分片上传断点续传)前后端实现(代码片段)

转载地址:https://segmentfault.com/a/1190000020963346         关于阿里云OSS的介绍请参考官方文档:阿里云OSS。出于账号安全的考虑,前端使用OSS服务需要走临时授权,即拿一个临时凭证(STSToken)去调用aliyun... 查看详情

前端实现大文件上传分片上传断点续传

总结一下大文件分片上传和断点续传的问题。因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况。http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件切片(分块)... 查看详情

大文件分片上传,断点续传,秒传实现

...PHP的上传文件限制不宜过大,一般5M左右为好;2,大文件分片,一片一片的传到服务端,再由 查看详情

csharp文件分片上传,断点续传

需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。 第一步:前端修改由于项目使用的是BJUI前端框架,... 查看详情

vue超大文件上传解决方案:分片断点上传

...所必须直面的。本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路。实现文件夹上传,要求:服务端保留层级结构,支持10w级别的文件夹上传。大文件上传及断点续传,要求:支持50G级的单个文... 查看详情

面试官:请你实现一个大文件上传和断点续传(代码片段)

...一段时间整理了下思路,那么究竟该如何实现一个大文件上传,以及在上传中如何实现断点续传的功能呢?本文将从零搭建前端和服务端,实现一个大文件上传和断点续 查看详情

java如何实现大文件分片上传,断点续传和秒传

Java如何实现大文件分片上传,断点续传和秒传引言概念秒传1、什么是秒传2、实现秒传常见做法分片上传1、什么是分片上传2、分片上传的场景断点续传1、什么是断点续传2、应用场景3、实现断点续传的核心逻辑实现思路前置知识... 查看详情

分块上传和断点续传(代码片段)

分块上传和断点续传两个概念分块上传:文件切成多块,独立传输,上传完成后合并断点续传:传输暂停或异常中断后,可基于原来进度重传几点说明:1、小文件不建议分块上传2、可以并行上传,并... 查看详情