今个要说的是文件上传组件:webuploader
先来简单说几个优点:以html5为主并在低版本的浏览器中支持flash模式;
兼容IE6+,iOS 6+, android 4+、分片并发、预览压缩、拖拽上传、md5秒传等等;
官网:http://fex.baidu.com/webuploader/
问答:https://github.com/fex-team/webuploader/issues
官网的文档写的很清楚了;自己都可以根据文档整合了;
这里之所以要写一篇博客是因为之前在thinkphp中深度整合了webuploader;
使用起来那是相当的简单方便;一次整合终身受益;
而且又不断的看到有童鞋们在整合的过程中碰壁;
这里就把整合好的代码分享出来以供参考;
示例项目:https://github.com/baijunyao/thinkphp-bjyadmin
一:导入webuploader
/Public/statics/webuploader-0.1.5
印象中是做了些什么修改的;但是年代久远;没有记录了;
建议直接复制项目中的这个文件夹;
二:添加自定义标签
/Application/Common/Tag/My.class.php
protected $tags=array( 'webuploadercss'=>array('attr'=>'','close'=>0), 'webuploader'=>array('attr'=>'name,url,word','close'=>0), 'webuploaderjs'=>array('attr'=>'','close'=>0), ); // webuploader 的css 部 分 和jquery 因 为 插 件 需 要 引 在jquery 后 边 ; 所 以 在 头 部 引 入 了jquery public function _webuploadercss(){ $str= <<< php < link rel = "stylesheet" href = "__PUBLIC__/statics/webuploader-0.1.5/xb-webuploader.css" > < script src = "//libs.useso.com/js/jquery/1.10.2/jquery.min.js" > </ script > php; return $str; } // webuploader 的js 部 分 public function _webuploaderjs(){ $str= <<< php < script > var BASE_URL = '__PUBLIC__/statics/webuploader-0.1.5'; </ script > < script src = "//cdn.staticfile.org/webuploader/0.1.5/webuploader.min.js" > </ script > php; return $str; } /** * 上 传 标 签 * @param string $tag * url : 上 传 的 图 片 处 理 的 控 制 器 方 法 * name : 表 单name * word : 提 示 文 字 */ public function _webuploader($tag){ $url=isset($tag['url'])?$tag['url']:U('Home/Index/ajax_upload'); $name=isset($tag['name'])?$tag['name']:'file_name'; $word=isset($tag['word'])?$tag['word']:' 或 将 照 片 拖 到 这 里 , 单 次 最 多 可 选300 张'; $id_name='upload-'.uniqid(); $str= <<< php < div id = "$id_name" class = "xb-uploader" > < div class = "queueList" > < div class = "placeholder" > < div class = "filePicker" > </ div > < p >$word </ p > </ div > </ div > < div class = "statusBar" style = "display:none;" > < div class = "progress" > < span class = "text" >0% </ span > < span class = "percentage" > </ span > </ div > < div class = "info" > </ div > < div class = "btns" > < div class = "webuploader-container filePicker2" > < div class = "webuploader-pick" > 继 续 添 加 </ div > < div style = "position: absolute; top: 0px; left: 0px; width: 1px; height: 1px; overflow: hidden;" id = "rt_rt_1armv2159g1o1i9c2a313hadij6" > </ div > </ div > < div class = "uploadBtn" > 开 始 上 传 </ div > </ div > </ div > </ div > < script > jQuery ( function ( ) { var \ $ = jQuery , // just in case. Make sure it's not an other libaray. \ $wrap = \ $ ( "#$id_name" ) , // 图片容器 \ $queue = \ $ ( '<ul class="filelist"></ul>' ) . appendTo ( \ $wrap . find ( '.queueList' ) ) , // 状态栏,包括进度和控制按钮 \ $statusBar = \ $wrap . find ( '.statusBar' ) , // 文件总体选择信息。 \ $info = \ $statusBar . find ( '.info' ) , // 上传按钮 \ $upload = \ $wrap . find ( '.uploadBtn' ) , // 没选择文件之前的内容。 \ $placeHolder = \ $wrap . find ( '.placeholder' ) , // 总体进度条 \ $progress = \ $statusBar . find ( '.progress' ) . hide ( ) , // 添加的文件数量 fileCount = 0 , // 添加的文件总大小 fileSize = 0 , // 优化retina, 在retina下这个值是2 ratio = window . devicePixelRatio || 1 , // 缩略图大小 thumbnailWidth = 110 * ratio , thumbnailHeight = 110 * ratio , // 可能有pedding, ready, uploading, confirm, done. state = 'pedding' , // 所有文件的进度信息,key为file id percentages = { } , supportTransition = ( function ( ) { var s = document . createElement ( 'p' ) . style , r = 'transition' in s || 'WebkitTransition' in s || 'MozTransition' in s || 'msTransition' in s || 'OTransition' in s ; s = null ; return r ; }) ( ) , thisSuccess , // WebUploader实例 uploader ; if ( ! WebUploader . Uploader . support ( ) ) { alert ( 'Web Uploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器' ) ; throw new Error ( 'WebUploader does not support the browser you are using.' ) ; } // 实例化 uploader = WebUploader . create ({ pick : { id : "#$id_name .filePicker" , label : '点击选择文件' , multiple : true } , dnd : "#$id_name .queueList" , paste : document . body , // accept: { // title: 'Images', // extensions: 'gif,jpg,jpeg,bmp,png', // mimeTypes: 'image/*' // }, // swf文件路径 swf : BASE_URL + '/Uploader.swf' , disableGlobalDnd : true , chunked : true , server : "$url" , fileNumLimit : 300 , fileSizeLimit : 200 * 1024 * 1024 , // 200 M fileSingleSizeLimit : 50 * 1024 * 1024 // 50 M }) ; // 添加“添加文件”的按钮, uploader . addButton ({ id : "#$id_name .filePicker2" , label : '继续添加' }) ; // 当有文件添加进来时执行,负责view的创建 function addFile ( file ) { var \ $li = \ $ ( '<li id="' + file . id + '">' + '<p class="title">' + file . name + '</p>' + '<p class="imgWrap"></p>' + '<p class="progress"><span></span></p>' + '<input class="bjy-filename" type="hidden" name="{$name}[]">' + '</li>' ) , \ $btns = \ $ ( '<div class="file-panel">' + '<span class="cancel">删除</span>' + '<span class="rotateRight">向右旋转</span>' + '<span class="rotateLeft">向左旋转</span></div>' ) . appendTo ( \ $li ) , \ $prgress = \ $li . find ( 'p.progress span' ) , \ $wrap = \ $li . find ( 'p.imgWrap' ) , \ $info = \ $ ( '<p class="error"></p>' ) , showError = function ( code ) { switch ( code ) { case 'exceed_size' : text = '文件大小超出' ; break ; case 'interrupt' : text = '上传暂停' ; break ; default : text = '上传失败,请重试' ; break ; } \ $info . text ( text ) . appendTo ( \ $li ) ; } ; if ( file . getStatus ( ) === 'invalid' ) { showError ( file . statusText ) ; } else { // @todo lazyload \ $wrap . text ( '预览中' ) ; uploader . makeThumb ( file , function ( error , src ) { if ( error ) { \ $wrap . text ( '不能预览' ) ; return ; } var img = \ $ ( '<img src="' + src + '">' ) ; \ $wrap . empty ( ) . append ( img ) ; } , thumbnailWidth , thumbnailHeight ) ; percentages [ file . id ] = [ file . size , 0 ] ; file . rotation = 0 ; } file . on ( 'statuschange' , function ( cur , prev ) { if ( prev === 'progress' ) { \ $prgress . hide ( ) . width ( 0 ) ; } else if ( prev === 'queued' ) { \ $li . off ( 'mouseenter mouseleave' ) ; \ $btns . remove ( ) ; } // 成功 if ( cur === 'error' || cur === 'invalid' ) { showError ( file . statusText ) ; percentages [ file . id ] [ 1 ] = 1 ; } else if ( cur === 'interrupt' ) { showError ( 'interrupt' ) ; } else if ( cur === 'queued' ) { percentages [ file . id ] [ 1 ] = 0 ; } else if ( cur === 'progress' ) { \ $info . remove ( ) ; \ $prgress . css ( 'display' , 'block' ) ; } else if ( cur === 'complete' ) { \ $li . append ( '<span class="success"></span>' ) ; } \ $li . removeClass ( 'state-' + prev ) . addClass ( 'state-' + cur ) ; }) ; \ $li . on ( 'mouseenter' , function ( ) { \ $btns . stop ( ) . animate ({ height : 30 }) ; }) ; \ $li . on ( 'mouseleave' , function ( ) { \ $btns . stop ( ) . animate ({ height : 0 }) ; }) ; \ $btns . on ( 'click' , 'span' , function ( ) { var index = \ $ ( this ) . index ( ) , deg ; switch ( index ) { case 0 : uploader . removeFile ( file ) ; return ; case 1 : file . rotation += 90 ; break ; case 2 : file . rotation -= 90 ; break ; } if ( supportTransition ) { deg = 'rotate(' + file . rotation + 'deg)' ; \ $wrap . css ({ '-webkit-transform' : deg , '-mos-transform' : deg , '-o-transform' : deg , 'transform' : deg }) ; } else { \ $wrap . css ( 'filter' , 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + ( ~~ (( file . rotation / 90 ) % 4 + 4 ) % 4 ) + ')' ) ; // use jquery animate to rotation // \$({ // rotation: rotation // }).animate({ // rotation: file.rotation // }, { // easing: 'linear', // step: function( now ) { // now = now * Math.PI / 180; // var cos = Math.cos( now ), // sin = Math.sin( now ); // \$wrap.css( 'filter', "progid:DXImageTransform.Microsoft.Matrix(M11=" + cos + ",M12=" + (-sin) + ",M21=" + sin + ",M22=" + cos + ",SizingMethod='auto expand')"); // } // }); } }) ; \ $li . appendTo ( \ $queue ) ; } // 负责view的销毁 function removeFile ( file ) { var \ $li = \ $ ( '#' + file . id ) ; delete percentages [ file . id ] ; updateTotalProgress ( ) ; \ $li . off ( ) . find ( '.file-panel' ) . off ( ) . end ( ) . remove ( ) ; } function updateTotalProgress ( ) { var loaded = 0 , total = 0 , spans = \ $progress . children ( ) , percent ; \ $ . each ( percentages , function ( k , v ) { total += v [ 0 ] ; loaded += v [ 0 ] * v [ 1 ] ; } ) ; percent = total ? loaded / total : 0 ; spans . eq ( 0 ) . text ( Math . round ( percent * 100 ) + '%' ) ; spans . eq ( 1 ) . css ( 'width' , Math . round ( percent * 100 ) + '%' ) ; updateStatus ( ) ; } function updateStatus ( ) { var text = '' , stats ; if ( state === 'ready' ) { text = '选中' + fileCount + '个文件,共' + WebUploader . formatSize ( fileSize ) + '。' ; } else if ( state === 'confirm' ) { stats = uploader . getStats ( ) ; if ( stats . uploadFailNum ) { text = '已成功上传' + stats . successNum + '个文件,' + stats . uploadFailNum + '个上传失败,<a class="retry" href="#">重新上传</a>失败文件或<a class="ignore" href="#">忽略</a>' } } else { stats = uploader . getStats ( ) ; text = '共' + fileCount + '个(' + WebUploader . formatSize ( fileSize ) + '),已上传' + stats . successNum + '个' ; if ( stats . uploadFailNum ) { text += ',失败' + stats . uploadFailNum + '个' ; } if ( fileCount == stats . successNum && stats . successNum != 0 ) { $ ( '#$id_name .webuploader-element-invisible' ) . remove ( ) ; } } \ $info . html ( text ) ; } uploader . onUploadAccept = function ( object , ret ) { if ( ret . error_info ) { fileError = ret . error_info ; return false ; } } uploader . onUploadSuccess = function ( file , response ) { \ $ ( '#' + file . id + ' .bjy-filename' ) . val ( response . name ) } uploader . onUploadError = function ( file ) { alert ( fileError ) ; } function setState ( val ) { var file , stats ; if ( val === state ) { return ; } \ $upload . removeClass ( 'state-' + state ) ; \ $upload . addClass ( 'state-' + val ) ; state = val ; switch ( state ) { case 'pedding' : \ $placeHolder . removeClass ( 'element-invisible' ) ; \ $queue . parent ( ) . removeClass ( 'filled' ) ; \ $queue . hide ( ) ; \ $statusBar . addClass ( 'element-invisible' ) ; uploader . refresh ( ) ; break ; case 'ready' : \ $placeHolder . addClass ( 'element-invisible' ) ; \ $ ( "#$id_name .filePicker2" ) . removeClass ( 'element-invisible' ) ; \ $queue . parent ( ) . addClass ( 'filled' ) ; \ $queue . show ( ) ; \ $statusBar . removeClass ( 'element-invisible' ) ; uploader . refresh ( ) ; break ; case 'uploading' : \ $ ( "#$id_name .filePicker2" ) . addClass ( 'element-invisible' ) ; \ $progress . show ( ) ; \ $upload . text ( '暂停上传' ) ; break ; case 'paused' : \ $progress . show ( ) ; \ $upload . text ( '继续上传' ) ; break ; case 'confirm' : \ $progress . hide ( ) ; \ $upload . text ( '开始上传' ) . addClass ( 'disabled' ) ; stats = uploader . getStats ( ) ; if ( stats . successNum && ! stats . uploadFailNum ) { setState ( 'finish' ) ; return ; } break ; case 'finish' : stats = uploader . getStats ( ) ; if ( stats . successNum ) { } else { // 没有成功的图片,重设 state = 'done' ; location . reload ( ) ; } break ; } updateStatus ( ) ; } uploader . onUploadProgress = function ( file , percentage ) { var \ $li = \ $ ( '#' + file . id ) , \ $percent = \ $li . find ( '.progress span' ) ; \ $percent . css ( 'width' , percentage * 100 + '%' ) ; percentages [ file . id ] [ 1 ] = percentage ; updateTotalProgress ( ) ; } ; uploader . onFileQueued = function ( file ) { fileCount ++ ; fileSize += file . size ; if ( fileCount === 1 ) { \ $placeHolder . addClass ( 'element-invisible' ) ; \ $statusBar . show ( ) ; } addFile ( file ) ; setState ( 'ready' ) ; updateTotalProgress ( ) ; } ; uploader . onFileDequeued = function ( file ) { fileCount -- ; fileSize -= file . size ; if ( ! fileCount ) { setState ( 'pedding' ) ; } removeFile ( file ) ; updateTotalProgress ( ) ; } ; uploader . on ( 'all' , function ( type ) { var stats ; switch ( type ) { case 'uploadFinished' : setState ( 'confirm' ) ; break ; case 'startUpload' : setState ( 'uploading' ) ; break ; case 'stopUpload' : setState ( 'paused' ) ; break ; } }) ; uploader . onError = function ( code ) { alert ( 'Eroor: ' + code ) ; } ; \ $upload . on ( 'click' , function ( ) { if ( \ $ ( this ) . hasClass ( 'disabled' ) ) { return false ; } if ( state === 'ready' ) { uploader . upload ( ) ; } else if ( state === 'paused' ) { uploader . upload ( ) ; } else if ( state === 'uploading' ) { uploader . stop ( ) ; } }) ; \ $info . on ( 'click' , '.retry' , function ( ) { uploader . retry ( ) ; } ) ; \ $info . on ( 'click' , '.ignore' , function ( ) { alert ( 'todo' ) ; } ) ; \ $upload . addClass ( 'state-' + state ) ; updateTotalProgress ( ) ; }) ; </ script > php; return $str; }三:接收上传文件的函数
/Application/Common/Common/function.php
/** * 上 传 文 件 类 型 控 制 此 方 法 仅 限ajax 上 传 使 用 * @param string $path 字 符 串 保 存 文 件 路 径 示 例 : /Upload/image/ * @param string $format 文 件 格 式 限 制 * @param integer $maxSize 允 许 的 上 传 文 件 最 大 值 52428800 * @return booler 返 回ajax 的json 格 式 数 据 */ function ajax_upload($path='file',$format='empty',$maxSize='52428800'){ ini_set('max_execution_time', '0'); // 去 除 两 边 的/ $path=trim($path,'/'); // 添 加Upload 根 目 录 $path=strtolower(substr($path, 0,6))==='upload' ? ucfirst($path) : 'Upload/'.$path; // 上 传 文 件 类 型 控 制 $ext_arr= array( 'image' => array('gif', 'jpg', 'jpeg', 'png', 'bmp'), 'photo' => array('jpg', 'jpeg', 'png'), 'flash' => array('swf', 'flv'), 'media' => array('swf', 'flv', 'mp3', 'wav', 'wma', 'wmv', 'mid', 'avi', 'mpg', 'asf', 'rm', 'rmvb'), 'file' => array('doc', 'docx', 'xls', 'xlsx', 'ppt', 'htm', 'html', 'txt', 'zip', 'rar', 'gz', 'bz2','pdf') ); if(!empty($_FILES)){ // 上 传 文 件 配 置 $config=array( 'maxSize' => $maxSize, // 上 传 文 件 最 大 为50M 'rootPath' => './', // 文 件 上 传 保 存 的 根 路 径 'savePath' => './'.$path.'/', // 文 件 上 传 的 保 存 路 径 ( 相 对 于 根 路 径 ) 'saveName' => array('uniqid',''), // 上 传 文 件 的 保 存 规 则 , 支 持 数 组 和 字 符 串 方 式 定 义 'autoSub' => true, // 自 动 使 用 子 目 录 保 存 上 传 文 件 默 认 为true 'exts' => isset($ext_arr[$format])?$ext_arr[$format]:'', ); // 实 例 化 上 传 $upload=new \Think\Upload($config); // 调 用 上 传 方 法 $info=$upload->upload(); $data=array(); if(!$info){ // 返 回 错 误 信 息 $error=$upload->getError(); $data['error_info']=$error; echo json_encode($data); }else{ // 返 回 成 功 信 息 foreach($info as $file){ $data['name']=trim($file['savepath'].$file['savename'],'.'); echo json_encode($data); } } } }四:接收上传文件的控制器方法
/Application/Home/Controller/IndexController.class.php
/** * webuploader 上 传 文 件 */ public function ajax_upload(){ // 根 据 自 己 的 业 务 调 整 上 传 路 径 、 允 许 的 格 式 、 文 件 大 小 ajax_upload('/Upload/image/'); } /** * webuploader 上 传demo */ public function webuploader(){ // 如 果 是post 提 交 则 显 示 上 传 的 文 件 否 则 显 示 上 传 页 面 if(IS_POST){ p($_POST);die; }else{ $this->display(); } } 五:html中使用 <!DOCTYPE html> < html > < head > < meta charset = "UTF-8" > < title >thinkphp 整 合 系 列 之webuploader 异 步 预 览 上 传 </ title > < webuploadercss /> </ head > < body > < form action = "{:U('Home/Index/webuploader')}" method = "post" > < webuploader name = "image" url = "{:U('Home/Index/ajax_upload')}" word = "或将照片拖到这里,单次最多可选300张" /> 自 定 义 附 加 字 段 : < input type = "text" name = "extend" value = "上传的文件描述" > < input type = "submit" value = "提交" > </ form > < webuploaderjs /> </ body > </ html >前面这么墨迹麻烦的整合了半天;最主要就是为了这一步的使用;
以后在需要上传的页面中;只需要添加三个标签就ok了;
在head中添加<webuploadercss />;
在body结束前添加 <webuploaderjs />;
在需要需要上传的表单中添加<webuploader />;
当然还可以自定义表单的名称、接收异步上传文件处理的url、提示文字;
<webuploader name="image" url="{:U('Home/Index/ajax_upload')}" word="或将照片拖到这里,单次最多可选300张" />
转自:白俊遥博客:http://baijunyao.com