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

    xiaoxiao2021-03-25  81

    文件上传是网站开发必不可少的,常见的有图片上传。但是大文件和视频上传不常见。这里我将自己写的视频上传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('<li id="'+file.id+'" class="upload_li">' + ' <img /> <span class="file_name upload_li">'+file.name+'</span></li><li class="upload_li"><span class="itemUpload weui-btn weui-btn_mini weui-btn_primary">上传</span><span class="itemStop weui-btn weui-btn_mini weui-btn_default">暂停</span><span class="itemDel weui-btn weui-btn_mini weui-btn_warn">删除</span></li><li class="upload_li">' + '<div id="percentage'+file.id+'" class="percentage"><div class="weui-progress__bar"><div class="weui-progress__inner-bar js_progress" style="width: 0%;"></div> <b id="pers"></b> </div></div>' + '</li>'); var $img = $("#" + file.id).find("img"); uploader.makeThumb(file, function(error, src){ if(error){ $img.replaceWith("<span class='no_view'>视频暂不能预览</span>"); } $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("<span style='color:green;'>上传完毕</span>"); $(".itemStop").hide(); $(".itemUpload").hide(); $(".itemDel").hide(); }else{ $(".percentage").find('#pers').html("<span style='color:red;'>上传失败,请您检查网络状况~</span>"); $(".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}'); }

    封装的上传类库

    <?php /** * * 版权所有:重庆市环境保护信息中心 * 作 者:Sqc * 日 期:2016-12-06 * 版 本:1.0.0 * 功能说明:用于视频等上传。 * **/ namespace Org\Util; Class Vupload { //要配置的内容 private $path = "./uploads"; private $allowtype = array('jpg', 'gif', 'png', 'mp4', 'mp3','3gp','rmvb','mov','avi','m4v'); private $maxsize = 104857600;// private $israndname = true; private $originName; private $tmpFileName; private $fileType; private $fileSize; private $newFileName; private $errorNum = 0; private $errorMess = ""; private $isChunk = false; private $indexOfChunk = 0; public function _initialize(){ parent::_initialize(); } /** * 用于设置成员属性($path, $allowtype, $maxsize, $israndname) * 可以通过连贯操作一次设置多个属性值 * @param $key 成员属性(不区分大小写) * @param $val 为成员属性设置的值 * @return object 返回自己对象$this, 可以用于连贯操作 */ function set($key, $val){ $key = strtolower($key); if (array_key_exists($key, get_class_vars(get_class($this)))){ $this->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 = "上传文件<font color='red'>{$this->originName}</font>时出错:"; 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."<br>"; } //根据文件的相关信息为分块数据创建文件夹 //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; } } }

    转载请注明原文地址: https://ju.6miu.com/read-22476.html

    最新回复(0)