文件上传是网站开发必不可少的,常见的有图片上传。但是大文件和视频上传不常见。这里我将自己写的视频上传demo贴出来供大家参考:
利用是最新的WebUploader插件请 下载使用最新版即可
js代码
[javascript] view plain copy
- _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控制器
[php] view plain copy
- 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] view plain copy
- <?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;
- }
- }
- }