利用WebUploader实现大文件上传和视频上传

文件上传是网站开发必不可少的,常见的有图片上传。但是大文件和视频上传不常见。这里我将自己写的视频上传demo贴出来供大家参考:

利用是最新的WebUploader插件请 下载使用最新版即可

js代码

_extensions ='3gp,mp4,rmvb,mov,avi,m4v';
    _mimeTypes ='video/*,audio/*,application/*';

$(function(){            
    var chunkSize = 500 * 1024;        //分块大小
    var uniqueFileName = null;          //文件唯一标识符
    var md5Mark = null;

    // var _backEndUrl = '';

    WebUploader.Uploader.register({
        "before-send-file": "beforeSendFile"
        , "before-send": "beforeSend"
        , "after-send-file": "afterSendFile"
    }, {
        beforeSendFile: function(file){
            console.log(file);
            //秒传验证
            var task = new $.Deferred();
            var start = new Date().getTime();
            (new WebUploader.Uploader()).md5File(file, 0, 10*1024*1024).progress(function(percentage){
            }).then(function(val){

                md5Mark = val;
                _userInfo.md5 = val;

                $.ajax({
                    type: "POST",
                    url: _backEndUrl,
                    data: {
                        status: "md5Check",
                        md5: val
                    },
                    cache: false,
                    timeout: 1000, //todo 超时的话,只能认为该文件不曾上传过
                    dataType: "json"
                }).then(function(data, textStatus, jqXHR){

                    if(data.ifExist){   //若存在,这返回失败给WebUploader,表明该文件不需要上传
                        task.reject();

                        uploader.skipFile(file);
                        file.path = data.path;
                        UploadComlate(file);
                    }else{
                        task.resolve();
                        //拿到上传文件的唯一名称,用于断点续传
                        uniqueFileName = md5(_userInfo.openid+_userInfo.time);
                    }
                }, function(jqXHR, textStatus, errorThrown){    //任何形式的验证失败,都触发重新上传
                    task.resolve();
                    //拿到上传文件的唯一名称,用于断点续传
                    uniqueFileName = md5(_userInfo.openid+_userInfo.time);
                });
            });
            return $.when(task);
        }
        , beforeSend: function(block){
            //分片验证是否已传过,用于断点续传
            var task = new $.Deferred();
            $.ajax({
                type: "POST"
                , url: _backEndUrl
                , data: {
                    status: "chunkCheck"
                    , name: uniqueFileName
                    , chunkIndex: block.chunk
                    , size: block.end - block.start
                }
                , cache: false
                , timeout: 1000 //todo 超时的话,只能认为该分片未上传过
                , dataType: "json"
            }).then(function(data, textStatus, jqXHR){
                if(data.ifExist){   //若存在,返回失败给WebUploader,表明该分块不需要上传
                    task.reject();
                }else{
                    task.resolve();
                }
            }, function(jqXHR, textStatus, errorThrown){    //任何形式的验证失败,都触发重新上传
                task.resolve();
            });

            return $.when(task);
        }
        , afterSendFile: function(file){
            var chunksTotal = 0;
            if((chunksTotal = Math.ceil(file.size/chunkSize)) > 1){
                //合并请求
                var task = new $.Deferred();
                $.ajax({
                    type: "POST"
                    , url: _backEndUrl
                    , data: {
                        status: "chunksMerge"
                        , name: uniqueFileName
                        , chunks: chunksTotal
                        , ext: file.ext
                        , md5: md5Mark
                    }
                    , cache: false
                    , dataType: "json"
                }).then(function(data, textStatus, jqXHR){

                    //todo 检查响应是否正常

                    task.resolve();
                    file.path = data.path;
                    UploadComlate(file);

                }, function(jqXHR, textStatus, errorThrown){
                    task.reject();
                });

                return $.when(task);
            }else{
                UploadComlate(file);
            }
        }
    });

	var uploader = WebUploader.create({
		swf: "./Uploader.swf",
        server: _backEndUrl,     //服务器处理文件的路径
        pick: "#picker",        //指定选择文件的按钮,此处放的是id
        resize: false, 
        dnd: "#theList",        //上传文件的拖拽容器(即,如果选择用拖拽的方式选择文件进行上传,应该要把文件拖拽到的区域容器)
        paste: document.body,   //[可选] [默认值:undefined]指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为document.body
        disableGlobalDnd: true, //[可选] [默认值:false]是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。
        compress: false,
        prepareNextFile: true, 
        chunked: true, 
        chunkSize: chunkSize,
        chunkRetry: 2,    //[可选] [默认值:2]如果某个分片由于网络问题出错,允许自动重传多少次?
        threads: true,      //[可选] [默认值:3] 上传并发数。允许同时最大上传进程数。
        formData: function(){return $.extend(true, {}, _userInfo);}, 
        fileNumLimit: 1, 
        fileSingleSizeLimit: 50 * 1024 * 1024,// 限制在50M
        duplicate: true,
        accept: {      
            title: '大文件上传',  //文字描述
            extensions: _extensions,     //允许的文件后缀,不带点,多个用逗号分割。,jpg,png,
            mimeTypes: _mimeTypes,      //多个用逗号分割。image/*,
        },
	});

    /**
     * 验证文件格式以及文件大小
     */
    uploader.on("error",function (type,handler){
        if (type=="Q_TYPE_DENIED"){
            swal({
                title:'',
                text: '请上传MP4格式的视频~',
                type: "warning",
                confirmButtonColor: "#DD6B55",
                confirmButtonText: "我知道了",
            });
        }else if(type=="F_EXCEED_SIZE"){
            swal({
                title:'',
                text: '视频大小不能超过50M哦~',
                type: "warning",
                confirmButtonColor: "#DD6B55",
                confirmButtonText: "我知道了",
            });
        }
    });

	uploader.on("fileQueued", function(file){
        $('#theList').show();
		$("#theList").append('
  • ' + ' '+file.name+'
  • 上传暂停删除
  • ' + '
    ' + '
  • '); var $img = $("#" + file.id).find("img"); uploader.makeThumb(file, function(error, src){ if(error){ $img.replaceWith("视频暂不能预览"); } $img.attr("src", src); }); }); $("#theList").on("click", ".itemUpload", function(){ uploader.upload(); //"上传"-->"暂停" $(this).hide(); $(".itemStop").css('display','inline-block'); $(".itemStop").show(); }); $("#theList").on("click", ".itemStop", function(){ uploader.stop(true); //"暂停"-->"上传" $(this).hide(); $(".itemUpload").show(); }); //todo 如果要删除的文件正在上传(包括暂停),则需要发送给后端一个请求用来清除服务器端的缓存文件 $("#theList").on("click", ".itemDel", function(){ uploader.removeFile($('.upload_li').attr("id")); //从上传文件列表中删除 $('.upload_li').remove(); //从上传列表dom中删除 }); uploader.on("uploadProgress", function(file, percentage){ $(".percentage").find('.js_progress').css("width",percentage * 100 + "%"); $(".percentage").find('#pers').text(parseInt(percentage * 100) + "%"); }); function UploadComlate(file){ console.log(file); if(file && file.name){ $('#vedio').val(file.name); $(".percentage").find('#pers').html("上传完毕"); $(".itemStop").hide(); $(".itemUpload").hide(); $(".itemDel").hide(); }else{ $(".percentage").find('#pers').html("上传失败,请您检查网络状况~"); $(".itemStop").hide(); $(".itemUpload").hide(); } } })


    PHP控制器

    public function vupload(){
            set_time_limit (0);
            //关闭缓存
            header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
            header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
            header("Cache-Control: no-store, no-cache, must-revalidate");
            header("Cache-Control: post-check=0, pre-check=0", false);
            header("Pragma: no-cache");
    
            $ip_path = './uploads/'.$_SESSION['userinfo']['id'];
            $save_path = 'uploads/'.$_SESSION['userinfo']['id'];
            $uploader =  new \Org\Util\Vupload;
            $uploader->set('path',$ip_path);
            //用于断点续传,验证指定分块是否已经存在,避免重复上传
            if(isset($_POST['status'])){
                if($_POST['status'] == 'chunkCheck'){
                    $target =  $ip_path.'/'.$_POST['name'].'/'.$_POST['chunkIndex'];
                    if(file_exists($target) && filesize($target) == $_POST['size']){
                        die('{"ifExist":1}');
                    }
                    die('{"ifExist":0}');
    
                }elseif($_POST['status'] == 'md5Check'){
    
                    //todo 模拟持久层查询
                    $dataArr = array(
                        'b0201e4d41b2eeefc7d3d355a44c6f5a' => 'kazaff2.jpg'
                    );
    
                    if(isset($dataArr[$_POST['md5']])){
                        die('{"ifExist":1, "path":"'.$dataArr[$_POST['md5']].'"}');
                    }
                    die('{"ifExist":0}');
                }elseif($_POST['status'] == 'chunksMerge'){
    
                    if($path = $uploader->chunksMerge($_POST['name'], $_POST['chunks'], $_POST['ext'])){
                        //todo 把md5签名存入持久层,供未来的秒传验证
                        session('video_path', $save_path.'/'.$path);
                        die('{"status":1, "path": "'.$save_path.'/'.$path.'"}');
                    }
                    die('{"status":0}');
                }
            }
    
            if(($path = $uploader->upload('file', $_POST)) !== false){
                if(!session('video_path')){
                    session('video_path', $save_path.'/'.$path);
                }
                die('{"status":1, "path": "'.$save_path.'/'.$path.'"}');
            }
            die('{"status":0}');
        }

    封装的上传类库


    setOption($key, $val);
            }
            return $this;
        }
    
        /**
         * 调用该方法上传文件
         * Enter description here ...
         * @param $fileField    上传文件的表单名称
         *
         */
        function upload($fileField, $info){
        
            //判断是否为分块上传
            $this->checkChunk($info);
            
            if (!$this->checkFilePath($this->path)){
                $this->errorMess = $this->getError();
                return false;
            }
    
            //将文件上传的信息取出赋给变量
            $name = $_FILES[$fileField]['name'];
            $tmp_name = $_FILES[$fileField]['tmp_name'];
            $size = $_FILES[$fileField]['size'];
            $error = $_FILES[$fileField]['error'];
    
    
            //设置文件信息
            if ($this->setFiles($name, $tmp_name, $size, $error)){
            
                //如果是分块,则创建一个唯一名称的文件夹用来保存该文件的所有分块
                if($this->isChunk){
                    $uploadDir = $this->path;
                    if($info){
                        $tmpName = $this->setDirNameForChunks();
                       
                         if(!$this->checkFilePath($uploadDir . '/' . $tmpName)){
                            $this->errorMess = $this->getError();
                            return false;
                        }
                    }
            //         $tmpName = $this->setDirNameForChunks($info);
            //         if(!$this->checkFilePath($uploadDir . '/' . $tmpName)){
                        // $this->errorMess = $this->getError();
            //          return false;
            //         }
                    
                    //创建一个对应的文件,用来记录上传分块文件的修改时间,用于清理长期未完成的垃圾分块
                    touch($uploadDir.'/'.$tmpName.'.tmp');
                }
    
                if($this->checkFileSize() && $this->checkFileType()){
                    $this->setNewFileName();
                    if ($this->copyFile()){
                        return $this->newFileName;
                    }
                }
            }
    
            $this->errorMess = $this->getError();
            return false;
        }
    
        public function chunksMerge($uniqueFileName, $chunksTotal, $fileExt){
            $targetDir = $this->path.'/'.$uniqueFileName;
            //检查对应文件夹中的分块文件数量是否和总数保持一致
            if($chunksTotal > 1 && (count(scandir($targetDir)) - 2) == $chunksTotal){
                //同步锁机制
                $lockFd = fopen($this->path.'/'.$uniqueFileName.'.lock', "w");
                if(!flock($lockFd, LOCK_EX | LOCK_NB)){
                    fclose($lockFd);
                    return false;
                }
    
                //进行合并
                $this->fileType = $fileExt;
                $finalName = $this->path.'/'.($this->setOption('newFileName', $this->proRandName()));
                $file = fopen($finalName, 'wb');
                for($index = 0; $index < $chunksTotal; $index++){
                    $tmpFile = $targetDir.'/'.$index;
                    $chunkFile = fopen($tmpFile, 'rb');
                    $content = fread($chunkFile, filesize($tmpFile));
                    fclose($chunkFile);
                    fwrite($file, $content);
    
                    //删除chunk文件
                    unlink($tmpFile);
                }
                fclose($file);
                //删除chunk文件夹
                rmdir($targetDir);
                unlink($this->path.'/'.$uniqueFileName.'.tmp');
    
                //解锁
                flock($lockFd, LOCK_UN);
                fclose($lockFd);
                unlink($this->path.'/'.$uniqueFileName.'.lock');
    
                return $this->newFileName;
    
            }
            return false;
        }
    
        //获取上传后的文件名称
        public function getFileName(){
            return $this->newFileName;
        }
    
        //上传失败后,调用该方法则返回,上传出错信息
        public function getErrorMsg(){
            return $this->errorMess;
        }
    
        //设置上传出错信息
        public function getError(){
            $str = "上传文件{$this->originName}时出错:";
            switch ($this->errorNum) {
                case 4:
                    $str.= "没有文件被上传";
                    break;
                case 3:
                    $str.= "文件只有部分被上传";
                    break;
                case 2:
                    $str.= "上传文件的大小超过了HTML表单中MAX_FILE_SIZE选项指定的值";
                    break;
                case 1:
                    $str.= "上传的文件超过了php.ini中upload_max_filesize选项限制的值";
                    break;
                case -1:
                    $str.= "未允许的类型";
                    break;
                case -2:
                    $str.= "文件过大, 上传的文件夹不能超过{$this->maxsize}个字节";
                    break;
                case -3:
                    $str.= "上传失败";
                    break;
                case -4:
                    $str.= "建立存放上传文件目录失败,请重新指定上传目录";
                    break;
                case -5:
                    $str.= "必须指定上传文件的路径";
                    break;
    
                default:
                    $str .= "未知错误";
            }
            return $str."
    "; } //根据文件的相关信息为分块数据创建文件夹 //md5(当前登录用户的数据库id + 文件原始名称 + 文件类型 + 文件最后修改时间 + 文件总大小) private function setDirNameForChunks(){ $str = $_SESSION['userinfo']['openid'].$_SESSION['userinfo']['report_time']; return md5($str); return $str; } //设置和$_FILES有关的内容 private function setFiles($name="", $tmp_name="", $size=0, $error=0){ $this->setOption('errorNum', $error); if ($error) { return false; } $this->setOption('originName', $name); $this->setOption('tmpFileName', $tmp_name); $aryStr = explode(".", $name); $this->setOption("fileType", strtolower($aryStr[count($aryStr)-1])); $this->setOption("fileSize", $size); return true; } private function checkChunk($info){ if(isset($info['chunks']) && $info['chunks'] > 0){ $this->setOption("isChunk", true); if(isset($info['chunk']) && $info['chunk'] >= 0){ $this->setOption("indexOfChunk", $info['chunk']); return true; } throw new Exception('分块索引不合法'); } return false; } //为单个成员属性设置值 private function setOption($key, $val){ $this->$key = $val; return $val; } //设置上传后的文件名称 private function setNewFileName(){ if($this->isChunk){ //如果是分块,则以分块的索引作为文件名称保存 $this->setOption('newFileName', $this->indexOfChunk); }elseif($this->israndname) { $this->setOption('newFileName', $this->proRandName()); }else{ $this->setOption('newFileName', $this->originName); } } //检查上传的文件是否是合法的类型 private function checkFileType(){ if (in_array(strtolower($this->fileType), $this->allowtype)) { return true; }else{ $this->setOption('errorNum', -1); return false; } } //检查上传的文件是否是允许的大小 private function checkFileSize(){ if ($this->fileSize > $this->maxsize) { $this->setOption('errorNum', -5); return false; }else{ return true; } } //检查是否有存放上传文件的目录 private function checkFilePath($target){ if (empty($target)) { $this->setOption('errorNum', -5); return false; } if (!file_exists($target) || !is_writable($target)) { if (!@mkdir($target, 0755)) { $this->setOption('errorNum', -4); return false; } } $this->path = $target; return true; } //设置随机文件名 private function proRandName(){ $fileName = date('YmdHis')."_".rand(100,999); return $fileName.'.'.$this->fileType; } //复制上传文件到指定的位置 private function copyFile(){ if (!$this->errorNum) { $path = rtrim($this->path, '/').'/'; $path.= $this->newFileName; if (@move_uploaded_file($this->tmpFileName, $path)) { return true; }else{ $this->setOption('errorNum', -3); return false; } }else{ return false; } } }



    你可能感兴趣的:(JavaScript)