基于php+webuploader的大文件分片上传,带进度条,支持断点续传(刷新、关闭页面、重新上传、网络中断等情况)。文件上传前先检测该文件是否已上传,如果已上传提示“文件已存在”,如果未上传则直接上传。视频上传时会根据设定的参数(分片大小、分片数量)进行上传,上传过程中会在目标文件夹中生成一个临时文件夹,用于存储临时分片,等所有分片上传完毕后,会根据序号重新组合成一个完整的视频,临时文件被删除。

如果文件上传至七牛云,可参看基于php大文件分片上传至七牛云,带进度条

首先下载webuploader

效果图:

临时文件,用于存储分片

html代码

<title>webuploader分片上传</title><meta charset="utf8"><!--引入CSS--><link rel="stylesheet" type="text/css" href="/static/webupload/webuploader.css"><script type="text/javascript" src="/static/index/js/jquery.js"></script><script type="text/javascript" src="/static/index/js/jquery.md5.js"></script><!--引入JS--><script type="text/javascript" src="/static/webupload/webuploader.js"></script><div id="uploader" class="wu-example">    <!--用来存放文件信息-->    <div id="thelist" class="uploader-list"></div>    <div class="btns">        <div id="picker">选择文件</div>        <button id="ctlBtn" class="btn btn-default">开始上传</button>    </div></div><style>    .progress{        height: 20px; width: 300px; background: #ccc; }    .progress-bar{        height: 20px; background: #0a3536;}</style><script>    var uploadswf = '/static/webupload/Uploader.swf';    var chunkSize = 2*1024*1024;    var server_url='uploadVedio';    var GUID = WebUploader.Base.guid();//一个GUID    var chunkObj = {};  //用来记录文件的状态、上传中断的位置    var seq=1;    var msg='';    $(function () {        var $ = jQuery;        var $list = $('#thelist');        WebUploader.Uploader.register({            "before-send-file":"beforeSendFile",            "before-send": "beforeSend"        }, {            "beforeSendFile": function (file) {                //上传前校验文件是否已经上传过                var deferred = WebUploader.Deferred();                $.ajax({                    type:"POST",                    //上传前校验文件上传到第几片                    url: "checkFile",                    data: {                        seq: seq,                        fileMd5: $.md5(file.name + file.size + file.ext),                        fileName:file.name                    },                    dataType: "json",                    success: function (data) {                        console.log(data);                        chunkObj = data;                        chunkObj.type = data.type;                        chunkObj.chunk == data.chunk;                        msg = data.msg;                        if(data.type==404){                            deferred.reject();                            $("#" + file.id).find(".state").text(data.msg);                        }else if (data.type == 0) {                            deferred.reject();                            $("#" + file.id).find(".state").text("文件已上传");                        } else if (data.type == 1) {                            if (data.chunk) {                                deferred.resolve();                            }                        } else {                            deferred.resolve();                        }                    },                    error: function () {                        deferred.resolve();                    }                })                //deferred.resolve();                return deferred.promise();            },            "beforeSend": function (block) {                var deferred = WebUploader.Deferred();                var curChunk = block.chunk;                var totalChunk = block.chunks;                console.log(chunkObj)                if (chunkObj.type == "1") {                    if (curChunk < chunkObj.chunk) {                        deferred.reject();                    } else {                        deferred.resolve();                    }                } else {                    deferred.resolve();                }                return deferred.promise();            }        });        var uploader = WebUploader.create({            // 选完文件后,是否自动上传。            auto: false,            // swf文件路径            swf: uploadswf,            // 文件接收服务端。            server: server_url,            // 内部根据当前运行是创建,可能是input元素,也可能是flash.            pick: '#picker',            chunked: true,//开始分片上传            chunkSize:1 * 1024 * 1024,//每一片的大小            threads: 1,            formData: {            },            // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!            resize: false        });        // 当有文件被添加进队列的时候        uploader.on('fileQueued', function (file) {            $list.append('' + file.id + '">' +                '
' + '

' + file.name + '

' + '

等待上传...

' + ''); }); // 文件上传过程中创建进度条实时显示。 uploader.on('uploadProgress', function (file, percentage) { var $li = $('#' + file.id), $percent = $li.find('.progress .progress-bar'); // 避免重复创建 if (!$percent.length) { $percent = $('
' + '
' + '' + '').appendTo($li).find('.progress-bar'); } $li.find('p.state').text('上传中'); $percent.css('width', percentage * 100 + '%'); }); uploader.on("uploadBeforeSend", function (obj, data, headers) { var file = obj.cuted.file; data.test = 1; data.fileMd5 = $.md5(file.name + file.size + file.ext); }) // 文件上传成功,给item添加成功class, 用样式标记上传成功。 uploader.on('uploadSuccess', function (file, response) { if(response.status==299){ $('#' + file.id).find('p.state').text('文件已存在'); }else{ $('#' + file.id).find('p.state').text('已上传'); } }); // 文件上传失败,显示上传出错。 uploader.on('uploadError', function (file) { $('#' + file.id).find('p.state').text(msg); }); // 完成上传完了,成功或者失败,先删除进度条。 uploader.on('uploadComplete', function (file) { $('#' + file.id).find('.progress').fadeOut(); }); //所有文件上传完毕 uploader.on("uploadFinished", function () { //提交表单 }); //开始上传 $("#ctlBtn").click(function () { uploader.upload(); }); });</script>

php请求后端

use app\index\controller\Upload;
public
function uploadVedio(){ $model =new Upload(); $res = $model->doUpload(); $model->ajaxReturn($res);}

封装上传类

<?phpnamespace app\index\controller;use think\Controller;/** * 大文件分片上传 */class Upload extends Controller{            private $filepath = 'uploads/'; //上传目录    private $blobNum; //第几个文件块    private $totalBlobNum; //文件块总数    private $fileName; //文件名    #允许上传的文件    private $allowExtension = ['mp4','avi','wmv'];    #文件后缀    private $fileExtension ='';    #当前块内容    private $nowFile = '';    #文件大小    private $totalSize = 0;    #文件总大小只允许1G    private $allowFileSize = 0;    #文件md5  前端传过来的   用于创建临时文件夹  上传完后删除    private $fileMd5='';    public function __construct($savePath =''){        $postData = $_POST;        #测试断点上传        if(isset($postData['test'])){            sleep(1);        }        if($savePath){            $this->filepath = $this->filepath.$savePath;        }        #    #文件名称      #  var_dump($postData);        $postData['name'] =isset($postData['name'])?$postData['name']:'';        $this->fileName =$postData['name'];        if($this->isHaveFile()){            $this->ajaxReturn(['status'=>299,'msg'=>'文件已存在!']);        }        $this->fileMd5 =$postData['fileMd5'];        #允许文件的大小  1G        $this->allowFileSize =(1*1024*1024*1024);        if((int)$postData['size']>$this->allowFileSize){            $this->ajaxReturn(['status'=>204,'msg'=>"文件大小超1G限制!"]);        }        #文件大小        $this->totalSize=$postData['size'];        $postData['chunks']=isset($postData['chunks'])?(int)$postData['chunks']:1;        $postData['chunk']=isset($postData['chunk'])?(int)$postData['chunk']:0;        if(!(int)$postData['chunks']){            $this->ajaxReturn(['status'=>208,'msg'=>'chunks参数错误']);        }        #当前块        $this->blobNum =$postData['chunk']+1;        #总共块        $this->totalBlobNum =$postData['chunks'];        #获取后缀        $fileExtension =explode(".",basename( $this->fileName));        $this->fileExtension=array_pop($fileExtension);        #检测后缀是否在允许范围        $this->checkFileExtension();        $this->nowFile =  $_FILES['file'];        if( $this->nowFile['error'] > 0) {            $msg['status'] = 502;            $msg['msg'] = "文件错误!";            $this->ajaxReturn($msg);        }    }    public function doUpload(){        #临时文件移动到指定目录下        $res = $this->moveFile();        if($res['status']==999){            return $this->fileMerge();        }else{            return $res;        }    }    #创建md5  文件名    public function createFileName(){        return $this->filepath.$this->fileName;    }    #检测文件是否重复    public function isHaveFile(){        if(file_exists($this->filepath.$this->fileName)){            return true;        }        return false;    }    #文件合并    public function fileMerge(){        if ($this->blobNum == $this->totalBlobNum) {            $fileName = $this->createFileName();            @unlink($fileName);            #删除旧文件            #文件合并  文件名以            $handle=fopen($fileName,"a+");            for($i=1; $i<= $this->totalBlobNum; $i++){                #当前分片数                $this->blobNum = $i;                #吧每个块的文件追加到 上传的文件中                fwrite($handle,file_get_contents($this->createBlockFileName()));            }            fclose($handle);            #删除分片            for($i=1; $i<= $this->totalBlobNum; $i++){                $this->blobNum = $i;                @unlink($this->createBlockFileName());            }            #删除临时目录            @rmdir($this->filepath.$this->fileMd5);            if(filesize($fileName) == $this->totalSize){                $msg['status'] = 200;                $msg['msg'] = '上传成功!';                $msg['size'] = $this->totalSize;                $msg['filename'] = "http://".$_SERVER['HTTP_HOST']."/".$this->createFileName();                $msg['name'] = $this->fileName;            }else{                $msg['status'] = 501;                $msg['msg'] = '上传文件大小和总大小有误!';                @unlink($this->createFileName());            }            return $msg;            # $this->ajaxReturn($msg);        }    }    #检测上传类型    public function checkFileExtension(){        if(!in_array(strtolower($this->fileExtension),$this->allowExtension)){            $this->ajaxReturn(['status'=>203,'msg'=>"文件类型不允许"]);        }    }    #将临时文件移动到指定目录    public function moveFile(){        try{            #每个块的文件名 以文件名的MD5作为命名            $filename=$this->createBlockFileName();            #分片文件写入            $handle=fopen($filename,"w+");            fwrite($handle,file_get_contents($this->nowFile ['tmp_name']));            fclose($handle);            #不是最后一块就返回当前信息   是最后一块往下执行合并操作            if($this->blobNum != $this->totalBlobNum) {                $msg['status'] = 201;                $msg['msg'] = "上传成功!";                $msg['blobNum'] = $this->blobNum;                return $msg;                #$this->ajaxReturn($msg);            }else{                $msg['status'] = 999;                $msg['msg'] = "上传成功!";                $msg['blobNum'] = $this->blobNum;                return $msg;            }        }catch (Exception $e){            $msg['status'] = 501;            $msg['error'] = $e->getMessage();            return $msg;            #$this->ajaxReturn($msg);        }    }    #创建分片文件名    public function createBlockFileName(){        $dirName = $this->filepath.$this->fileMd5."/";        if (!is_dir($dirName) ) {            @mkdir($dirName, 0700);        };        return $dirName.$this->blobNum.".part";    }    #json格式放回处理    public function ajaxReturn($msg){        exit(json_encode($msg));    }}

——现在的努力,只为小时候吹过的牛逼! ——