function chunk_file ( name , accept , disk , driver ) { var initVal = $ ( '#' + name + '-savedpath' ).val (); initVal = initVal ? true : false; var $wrap = $ ( '#uploader' + name ) , // 图片容器 $queue = $ ( '' ) .appendTo ( $wrap.find ( '#queueList' + name ) ) , // 状态栏,包括进度和控制按钮 $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 = {} , // 判断浏览器是否支持图片的base64 isSupportBase64 = (function () { var data = new Image (); var support = true; data.onload = data.onerror = function () { if ( this.width != 1 || this.height != 1 ) { support = false; } } data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; return support; }) () , // 检测是否已经安装flash,检测flash的版本 flashVersion = (function () { var version; try { version = navigator.plugins['Shockwave Flash']; version = version.description; } catch ( ex ) { try { version = new ActiveXObject ( 'ShockwaveFlash.ShockwaveFlash' ) .GetVariable ( '$version' ); } catch ( ex2 ) { version = '0.0'; } } version = version.match ( /\d+/g ); return parseFloat ( version[0] + '.' + version[1] , 10 ); }) () , 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; }) () , // WebUploader实例 uploader; if ( !WebUploader.Uploader.support ( 'flash' ) && WebUploader.browser.ie ) { // flash 安装了但是版本过低。 if ( flashVersion ) { (function ( container ) { window['expressinstallcallback'] = function ( state ) { switch ( state ) { case 'Download.Cancelled': swal ( '您取消了更新!' , '' , 'error' ).then ( function () { } ); break; case 'Download.Failed': swal ( '安装失败!' , '' , 'error' ).then ( function () { } ); break; default: swal ( '安装已成功,请刷新页面!' , '' , 'error' ).then ( function () { } ); break; } delete window['expressinstallcallback']; }; var swf = './expressInstall.swf'; // insert flash object var html = '' + '' + '' + ''; container.html ( html ); }) ( $wrap ); // 压根就没有安转。 } else { $wrap.html ( 'get flash player' ); } return; } else if ( !WebUploader.Uploader.support () ) { swal ( 'Web Uploader 不支持您的浏览器!' , '' , 'error' ).then ( function () { } ); return; } var options = { tokenUrl: window.chunk_file.prefix + '/' + "chunk-file-upload/get_qiniu_token" , mockToken: false , hash: false , disk: disk }; if ( driver == 'local' ) {//本地 options.host = window.chunk_file.prefix + '/' + 'chunk-file-upload/upload'; } else if ( driver = 'qiniu' ) { options.host = window.chunk_file.area; } var uploader = WebUploader.create ( { swf: "Uploader.swf" , server: options.host , pick: { id: '#filePicker' + name , label: '点击选择文件' } , duplicate: true , resize: false , dnd: "#dndArea" + name , paste: document.body , disableGlobalDnd: true , thumb: { width: 100 , height: 100 , quality: 70 , allowMagnify: true , crop: true } , accept: accept , compress: false , prepareNextFile: true , chunked: true , chunkSize: 4 * 1024 * 1024 , threads: 5 , fileNumLimit: window.chunk_file.fileNumLimit , fileSizeLimit: window.chunk_file.fileSizeLimit , // duplicate: false } ); var token; var m = new Map (); // 添加“添加文件”的按钮, if ( window.chunk_file.fileNumLimit <= 1 ) { uploader.addButton ( { id: '#filePicker2' + name , label: '重新选择' } ); } else { uploader.addButton ( { id: '#filePicker2' + name , label: '继续选择' } ); } //只能单传图片 uploader.on ( "beforeFileQueued" , function ( file ) { if ( window.chunk_file.fileNumLimit <= 1 ) {//单传 if ( $ ( 'ul.filelist li' ).length ) { var file_id = $ ( 'ul.filelist' ).children ( 'li' ).attr ( 'id' ); uploader.removeFile ( file_id ); } } } ); //错误提示 uploader.on ( "error" , function ( type , handler ) { switch ( type ) { case 'Q_EXCEED_NUM_LIMIT': swal ( '上传文件总数量不能超过' + uploader.options.fileNumLimit + '个' , '' , 'error' ).then ( function () { } ); break; case 'Q_EXCEED_SIZE_LIMIT': swal ( '上传文件大小不能超过' + uploader.options.fileSizeLimit + "KB" , '' , 'error' ).then ( function () { } ); break; case 'Q_TYPE_DENIED': swal ( '上传文件类型不被允许' , '' , 'error' ).then ( function () { } ); break; } } ); //当文件被加入队列以后触发。 uploader.on ( "fileQueued" , function ( file ) { fileCount++; fileSize += file.size; var ctx = new Array (); m.set ( file.name , ctx ); if ( fileCount === 1 ) { $placeHolder.addClass ( 'element-invisible' ); $statusBar.show (); } addFile ( file ); setState ( 'ready' ); updateTotalProgress (); } ); uploader.on ( "uploadStart" , function ( file ) { if ( driver == 'qiniu' ) {//七牛,传递七牛key if ( !options.mockToken ) { GetToken ( options.tokenUrl , file ); } else { uploader.options.formData = { token: options.mockTokenValue }; token = options.mockTokenValue; } } else {//laravel传递laravelkey uploader.options.formData._token = $ ( 'meta[name="csrf-token"]' ).attr ( 'content' ); } var name = Date.parse ( new Date () ) + Math.random ().toString ( 36 ).substr ( 2 ) + '.' + file.source.ext; uploader.options.formData.key = name; uploader.options.formData.disk = options.disk; } ); uploader.on ( "uploadBeforeSend" , function ( block , data , headers ) { if ( parseInt ( block.file.size ) <= parseInt ( uploader.options.chunkSize ) ) { uploader.options.chunked = false; } else { uploader.options.chunked = true; headers['Authorization'] = 'UpToken ' + token; headers['Content-Type'] = 'application/octet-stream'; block.transport.options.server = options.host + "/mkblk/" + (block.end - block.start); block.transport.options.sendAsBinary = true; block.transport.options.formData = false; } } ); 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.on ( "uploadAccept" , function ( block , ret ) { //ctx[block.chunk] = ret.ctx; m.get ( block.file.name )[block.chunk] = ret.ctx; } ); uploader.on ( "uploadSuccess" , function ( file , res ) { //成功之后,给li加上key值 $ ( '#' + file.id ).attr ( 'dataSrc' , res.key ); if ( parseInt ( file.size ) <= parseInt ( uploader.options.chunkSize ) ) {//大于就分片,小于就完毕 UploadComplete ( file , res ); } else { MakeFile ( m.get ( file.name ) , file , options.hash ); } } ); uploader.on ( 'all' , function ( type ) { var stats; switch ( type ) { case 'uploadFinished'://都结束了 $ ( '.form-horizontal button[type="submit"]' ).attr ( 'disabled' , false ).css ( { "cursor": "pointer" } ).attr ( 'title' , '' ); setState ( 'confirm' ); break; case 'startUpload'://开始上传 $ ( '.form-horizontal button[type="submit"]' ).attr ( 'disabled' , true ).css ( { "cursor": " no-drop" } ).attr ( 'title' , '上传文件中...' ); setState ( 'uploading' ); break; case 'stopUpload'://暂停上传 $ ( '.form-horizontal button[type="submit"]' ).attr ( 'disabled' , true ).css ( { "cursor": " no-drop" } ).attr ( 'title' , '上传文件暂停...' ); setState ( 'paused' ); break; } } ); uploader.on ( "fileDequeued" , function ( file , res ) { fileCount--; fileSize -= file.size; if ( !fileCount ) { setState ( 'pedding' ); } removeFile ( file ); updateTotalProgress (); } ); $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 ( true ); } } ); $info.on ( 'click' , '.retry' , function () { uploader.retry (); } ); $info.on ( 'click' , '.ignore' , function () { swal ( '已忽略' , '' , 'error' ).then ( function () { } ); } ); $upload.addClass ( 'state-' + state ); function GetToken ( tokenUrl , file ) { $.ajax ( { async: false , type: 'get' , url: tokenUrl , dataType: 'json' , data: { disk: disk } , success: function ( res ) { if ( res.code ) { token = res.uptoken; if ( options.hash ) { uploader.options.formData = { token: token , } } else { uploader.options.formData = { token: token , key: file.name } } } else { swal ( res.msg , '' , 'error' ).then ( function () { } ); throw '获取token错误'; } } } ); } function MakeFile ( ctx , file , hash ) { var b = ctx.join ( "," ); if ( hash ) {//key固定 $.ajax ( { headers: { 'X-CSRF-TOKEN': $ ( 'meta[name="csrf-token"]' ).attr ( 'content' ) , 'file-id': file.id , 'file-ext': file.source.ext , 'disk': disk } , type: 'POST' , url: options.host + '/mkfile/' + file.size , data: b , contentType: "text/plain" , contentLength: b.length , beforeSend: function ( XMLHttpRequest ) { XMLHttpRequest.setRequestHeader ( "Authorization" , 'UpToken ' + token ); } , success: function ( res ) { UploadComplete ( file , res ); } } ); } else {//key不固定 $.ajax ( { headers: { 'X-CSRF-TOKEN': $ ( 'meta[name="csrf-token"]' ).attr ( 'content' ) , 'file-id': file.id , 'file-ext': file.source.ext , 'disk': disk } , type: 'POST' , url: options.host + '/mkfile/' + file.size + '/key/' + URLSafeBase64Encode ( uploader.options.formData.key ) , data: b , contentType: "text/plain" , contentLength: b.length , beforeSend: function ( XMLHttpRequest ) { XMLHttpRequest.setRequestHeader ( "Authorization" , 'UpToken ' + token ); } , success: function ( res ) { UploadComplete ( file , res ); } } ); } } function UploadComplete ( file , res ) { if ( window.chunk_file.fileNumLimit <= 1 ) { $ ( '#' + name + '-savedpath' ).val ( window.chunk_file.url + '/' + res.key ); } else { if ( window.chunk_file.saveType == 'json' ) {//为json类型 if ( initVal ) {//如果初始有数据 $ ( '#' + name + '-savedpath' ).val ( '' ); initVal = false;//然后置false } //先拿出来 var data = $ ( '#' + name + '-savedpath' ).val (); if ( !data ) {//为空 data = new Array (); data.push ( res.key ); } else { try { data = JSON.parse ( data ); } catch ( e ) { data = new Array (); } data.push ( res.key ); } $ ( '#' + name + '-savedpath' ).val ( JSON.stringify ( data ) ); } } ctx = new Array (); uploader.options.chunked = true; } function URLSafeBase64Decode ( data ) { data = data.replace ( /_/g , '/' ).replace ( /-/g , '+' ); var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var o1 , o2 , o3 , h1 , h2 , h3 , h4 , bits , i = 0 , ac = 0 , dec = "" , tmp_arr = []; if ( !data ) { return data; } data += ''; do { // unpack four hexets into three octets using index points in b64 h1 = b64.indexOf ( data.charAt ( i++ ) ); h2 = b64.indexOf ( data.charAt ( i++ ) ); h3 = b64.indexOf ( data.charAt ( i++ ) ); h4 = b64.indexOf ( data.charAt ( i++ ) ); bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; o1 = bits >> 16 & 0xff; o2 = bits >> 8 & 0xff; o3 = bits & 0xff; if ( h3 === 64 ) { tmp_arr[ac++] = String.fromCharCode ( o1 ); } else if ( h4 === 64 ) { tmp_arr[ac++] = String.fromCharCode ( o1 , o2 ); } else { tmp_arr[ac++] = String.fromCharCode ( o1 , o2 , o3 ); } } while ( i < data.length ); dec = tmp_arr.join ( '' ); return dec; } function utf8_encode ( argString ) { if ( argString === null || typeof argString === 'undefined' ) { return ''; } var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n'); var utftext = '' , start , end , stringl = 0; start = end = 0; stringl = string.length; for ( var n = 0 ; n < stringl ; n++ ) { var c1 = string.charCodeAt ( n ); var enc = null; if ( c1 < 128 ) { end++; } else if ( c1 > 127 && c1 < 2048 ) { enc = String.fromCharCode ( (c1 >> 6) | 192 , (c1 & 63) | 128 ); } else if ( c1 & 0xF800 ^ 0xD800 > 0 ) { enc = String.fromCharCode ( (c1 >> 12) | 224 , ((c1 >> 6) & 63) | 128 , (c1 & 63) | 128 ); } else { // surrogate pairs if ( c1 & 0xFC00 ^ 0xD800 > 0 ) { throw new RangeError ( 'Unmatched trail surrogate at ' + n ); } var c2 = string.charCodeAt ( ++n ); if ( c2 & 0xFC00 ^ 0xDC00 > 0 ) { throw new RangeError ( 'Unmatched lead surrogate at ' + (n - 1) ); } c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; enc = String.fromCharCode ( (c1 >> 18) | 240 , ((c1 >> 12) & 63) | 128 , ((c1 >> 6) & 63) | 128 , (c1 & 63) | 128 ); } if ( enc !== null ) { if ( end > start ) { utftext += string.slice ( start , end ); } utftext += enc; start = end = n + 1; } } if ( end > start ) { utftext += string.slice ( start , stringl ); } return utftext; } function URLSafeBase64Encode ( data ) { var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var o1 , o2 , o3 , h1 , h2 , h3 , h4 , bits , i = 0 , ac = 0 , enc = '' , tmp_arr = []; if ( !data ) { return data; } data = utf8_encode ( data + '' ); do { // pack three octets into four hexets o1 = data.charCodeAt ( i++ ); o2 = data.charCodeAt ( i++ ); o3 = data.charCodeAt ( i++ ); bits = o1 << 16 | o2 << 8 | o3; h1 = bits >> 18 & 0x3f; h2 = bits >> 12 & 0x3f; h3 = bits >> 6 & 0x3f; h4 = bits & 0x3f; // use hexets to index into b64, and append result to encoded string tmp_arr[ac++] = b64.charAt ( h1 ) + b64.charAt ( h2 ) + b64.charAt ( h3 ) + b64.charAt ( h4 ); } while ( i < data.length ); enc = tmp_arr.join ( '' ); switch ( data.length % 3 ) { case 1: enc = enc.slice ( 0 , -2 ) + '=='; break; case 2: enc = enc.slice ( 0 , -1 ) + '='; break; } return enc.replace ( /\//g , '_' ).replace ( /\+/g , '-' ); } // 当有文件添加进来时执行,负责view的创建 function addFile ( file ) { var $li = $ ( '
  • ' + '

    ' + file.name + '

    ' + '

    ' + '

    ' + '
  • ' ) , $btns = $ ( '
    ' + '删除' + '向右旋转' + '向左旋转
    ' ).appendTo ( $li ) , $prgress = $li.find ( 'p.progress span' ) , $wrap = $li.find ( 'p.imgWrap' ) , $info = $ ( '

    ' ) , 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 ) { var img; if ( error ) { $wrap.text ( '不能预览' ); return; } if ( isSupportBase64 ) { img = $ ( '' ); $wrap.empty ().append ( img ); } else { $wrap.text ( "预览出错" ); } } , 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 ( '' ); } $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) + ')' ); } } ); $li.appendTo ( $queue ); } // 负责view的销毁 function removeFile ( file ) { var $li = $ ( '#' + file.id ); delete percentages[file.id]; updateTotalProgress (); $li.off ().find ( '.file-panel' ).off ().end ().remove (); //然后删除数组文件 var dataSrc = $li.attr ( 'dataSrc' ); var data = $ ( '#' + name + '-savedpath' ).val (); if ( data ) {//不为空 try { data = JSON.parse ( data ); } catch ( e ) { data = new Array (); } for ( var i = 0 ; i < data.length ; i++ ) { if ( dataSrc == data[i] ) { data.splice ( i , 1 ); } } if ( !data.length ) { data = ''; } else { data = JSON.stringify ( data ); } $ ( '#' + name + '-savedpath' ).val ( data ); } } 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.hide (); $statusBar.addClass ( 'element-invisible' ); uploader.refresh (); break; case 'ready': $placeHolder.addClass ( 'element-invisible' ); $ ( '#filePicker2' + name ).removeClass ( 'element-invisible' ); $queue.show (); $statusBar.removeClass ( 'element-invisible' ); uploader.refresh (); break; case 'uploading': $ ( '#filePicker2' + name ).addClass ( 'element-invisible' ); $progress.show (); $upload.text ( '暂停上传' ); break; case 'paused': $progress.show (); $upload.text ( '继续上传' ); break; case 'confirm': $progress.hide (); $ ( '#filePicker2' + name ).removeClass ( 'element-invisible' ); $upload.text ( '开始上传' ); stats = uploader.getStats (); if ( stats.successNum && !stats.uploadFailNum ) { setState ( 'finish' ); return; } break; case 'finish': stats = uploader.getStats (); if ( stats.successNum ) { swal ( '上传成功' , '' , 'success' ).then ( function () { } ); } else { // 没有成功的图片,重设 state = 'done'; location.reload (); } break; } 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 + '张照片至XX相册,' + stats.uploadFailNum + '张照片上传失败,重新上传失败图片或忽略' } } else { stats = uploader.getStats (); text = '共' + fileCount + '张(' + WebUploader.formatSize ( fileSize ) + '),已上传' + stats.successNum + '张'; if ( stats.uploadFailNum ) { text += ',失败' + stats.uploadFailNum + '张'; } } $info.html ( text ); } 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 (); } $ ( '#' + name + '-savedpath' ).css ( 'cursor' , 'pointer' ) $ ( document ).on ( 'click' , '#' + name + '-savedpath' , function () { window.open ( $ ( this ).val () ); } ); return uploader; }