├── .gitattributes ├── README.md ├── Uploader.swf ├── demo.html ├── jquery-1.10.2.min.js ├── md5.js ├── serverDubbox ├── pom.xml ├── resources │ ├── META-INF │ │ └── spring │ │ │ ├── application.xml │ │ │ └── provider.xml │ ├── configure.properties │ ├── dubbo.properties │ └── log4j.xml └── src │ └── main │ └── java │ └── me │ └── kazaff │ ├── Main.java │ └── wu │ ├── controller │ ├── FileUploadeService.java │ └── FileUploader.java │ ├── entity │ ├── FileInfo.java │ ├── FileUploadForm.java │ └── Result.java │ ├── extension │ └── CORSFilter.java │ ├── service │ └── webUploader.java │ └── util │ └── fileLock.java ├── serverJAVAEE ├── pom.xml └── src │ ├── main │ ├── java │ │ └── me │ │ │ └── kazaff │ │ │ ├── filters │ │ │ └── CORSFilter.java │ │ │ └── wu │ │ │ ├── controller │ │ │ └── FileUploadController.java │ │ │ ├── entity │ │ │ └── FileInfo.java │ │ │ ├── service │ │ │ └── webUploader.java │ │ │ └── util │ │ │ └── fileLock.java │ └── webapp │ │ └── WEB-INF │ │ ├── configure.properties │ │ ├── logback.xml │ │ ├── mvc-dispatcher-servlet.xml │ │ ├── pages │ │ ├── error_fileupload.jsp │ │ └── hello.jsp │ │ └── web.xml │ └── test │ └── java │ └── me │ └── kazaff │ └── wu │ └── AppTests.java ├── serverNODE ├── app.js ├── config.js ├── package.json └── webuploader.js ├── serverPHP └── fileUpload.php ├── webuploader.css ├── webuploader.custom.js ├── webuploader.custom.min.js ├── webuploader.fis.js ├── webuploader.flashonly.js ├── webuploader.flashonly.min.js ├── webuploader.html5only.js ├── webuploader.html5only.min.js ├── webuploader.js ├── webuploader.min.js ├── webuploader.noimage.js ├── webuploader.noimage.min.js ├── webuploader.nolog.js ├── webuploader.nolog.min.js ├── webuploader.withoutimage.js └── webuploader.withoutimage.min.js /.gitattributes: -------------------------------------------------------------------------------- 1 | .* 2 | *.iml 3 | .idea/ 4 | uploads/ 5 | serverNode/node_modules/ 6 | serverNode/tmp/ 7 | serverJAVAEE/.idea/ 8 | serverJAVAEE/target/ 9 | serverJAVAEE/serverJAVAEE.iml 10 | /serverDubbox/target 11 | /serverDubbox/log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 说明 2 | --- 3 | 4 | 因为原始webuploader.js不支持为formData设置函数类型参数,这将导致不能在控件初始化后修改该参数,所以修改了对应源码,大概在webuploader.js源码的[3170](https://github.com/fex-team/webuploader/blob/master/dist/webuploader.js#L3170)行。 5 | 6 | 具体细节请参看[这里](http://blog.kazaff.me/2014/11/14/%E8%81%8A%E8%81%8A%E5%A4%A7%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0/)。 7 | -------------------------------------------------------------------------------- /Uploader.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kazaff/webuploaderDemo/4d3d25d53736004cca99ad99e699cfb07ddf8a6a/Uploader.swf -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebUploader 6 | 7 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
选择文件
30 |
31 | 32 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /md5.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JavaScript MD5 1.0.1 3 | * https://github.com/blueimp/JavaScript-MD5 4 | * 5 | * Copyright 2011, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | * 11 | * Based on 12 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message 13 | * Digest Algorithm, as defined in RFC 1321. 14 | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 15 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 16 | * Distributed under the BSD License 17 | * See http://pajhome.org.uk/crypt/md5 for more info. 18 | */ 19 | 20 | /*jslint bitwise: true */ 21 | /*global unescape, define */ 22 | 23 | (function ($) { 24 | 'use strict'; 25 | 26 | /* 27 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 28 | * to work around bugs in some JS interpreters. 29 | */ 30 | function safe_add(x, y) { 31 | var lsw = (x & 0xFFFF) + (y & 0xFFFF), 32 | msw = (x >> 16) + (y >> 16) + (lsw >> 16); 33 | return (msw << 16) | (lsw & 0xFFFF); 34 | } 35 | 36 | /* 37 | * Bitwise rotate a 32-bit number to the left. 38 | */ 39 | function bit_rol(num, cnt) { 40 | return (num << cnt) | (num >>> (32 - cnt)); 41 | } 42 | 43 | /* 44 | * These functions implement the four basic operations the algorithm uses. 45 | */ 46 | function md5_cmn(q, a, b, x, s, t) { 47 | return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b); 48 | } 49 | function md5_ff(a, b, c, d, x, s, t) { 50 | return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); 51 | } 52 | function md5_gg(a, b, c, d, x, s, t) { 53 | return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); 54 | } 55 | function md5_hh(a, b, c, d, x, s, t) { 56 | return md5_cmn(b ^ c ^ d, a, b, x, s, t); 57 | } 58 | function md5_ii(a, b, c, d, x, s, t) { 59 | return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); 60 | } 61 | 62 | /* 63 | * Calculate the MD5 of an array of little-endian words, and a bit length. 64 | */ 65 | function binl_md5(x, len) { 66 | /* append padding */ 67 | x[len >> 5] |= 0x80 << (len % 32); 68 | x[(((len + 64) >>> 9) << 4) + 14] = len; 69 | 70 | var i, olda, oldb, oldc, oldd, 71 | a = 1732584193, 72 | b = -271733879, 73 | c = -1732584194, 74 | d = 271733878; 75 | 76 | for (i = 0; i < x.length; i += 16) { 77 | olda = a; 78 | oldb = b; 79 | oldc = c; 80 | oldd = d; 81 | 82 | a = md5_ff(a, b, c, d, x[i], 7, -680876936); 83 | d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586); 84 | c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819); 85 | b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); 86 | a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897); 87 | d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); 88 | c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); 89 | b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983); 90 | a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); 91 | d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); 92 | c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); 93 | b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); 94 | a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); 95 | d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); 96 | c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); 97 | b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); 98 | 99 | a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510); 100 | d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); 101 | c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713); 102 | b = md5_gg(b, c, d, a, x[i], 20, -373897302); 103 | a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691); 104 | d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083); 105 | c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); 106 | b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848); 107 | a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438); 108 | d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); 109 | c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961); 110 | b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); 111 | a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); 112 | d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784); 113 | c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); 114 | b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); 115 | 116 | a = md5_hh(a, b, c, d, x[i + 5], 4, -378558); 117 | d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); 118 | c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); 119 | b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); 120 | a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); 121 | d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); 122 | c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632); 123 | b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); 124 | a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174); 125 | d = md5_hh(d, a, b, c, x[i], 11, -358537222); 126 | c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979); 127 | b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189); 128 | a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487); 129 | d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); 130 | c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520); 131 | b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651); 132 | 133 | a = md5_ii(a, b, c, d, x[i], 6, -198630844); 134 | d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); 135 | c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); 136 | b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055); 137 | a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); 138 | d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); 139 | c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); 140 | b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); 141 | a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); 142 | d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); 143 | c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); 144 | b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); 145 | a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070); 146 | d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); 147 | c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259); 148 | b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551); 149 | 150 | a = safe_add(a, olda); 151 | b = safe_add(b, oldb); 152 | c = safe_add(c, oldc); 153 | d = safe_add(d, oldd); 154 | } 155 | return [a, b, c, d]; 156 | } 157 | 158 | /* 159 | * Convert an array of little-endian words to a string 160 | */ 161 | function binl2rstr(input) { 162 | var i, 163 | output = ''; 164 | for (i = 0; i < input.length * 32; i += 8) { 165 | output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF); 166 | } 167 | return output; 168 | } 169 | 170 | /* 171 | * Convert a raw string to an array of little-endian words 172 | * Characters >255 have their high-byte silently ignored. 173 | */ 174 | function rstr2binl(input) { 175 | var i, 176 | output = []; 177 | output[(input.length >> 2) - 1] = undefined; 178 | for (i = 0; i < output.length; i += 1) { 179 | output[i] = 0; 180 | } 181 | for (i = 0; i < input.length * 8; i += 8) { 182 | output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32); 183 | } 184 | return output; 185 | } 186 | 187 | /* 188 | * Calculate the MD5 of a raw string 189 | */ 190 | function rstr_md5(s) { 191 | return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); 192 | } 193 | 194 | /* 195 | * Calculate the HMAC-MD5, of a key and some data (raw strings) 196 | */ 197 | function rstr_hmac_md5(key, data) { 198 | var i, 199 | bkey = rstr2binl(key), 200 | ipad = [], 201 | opad = [], 202 | hash; 203 | ipad[15] = opad[15] = undefined; 204 | if (bkey.length > 16) { 205 | bkey = binl_md5(bkey, key.length * 8); 206 | } 207 | for (i = 0; i < 16; i += 1) { 208 | ipad[i] = bkey[i] ^ 0x36363636; 209 | opad[i] = bkey[i] ^ 0x5C5C5C5C; 210 | } 211 | hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); 212 | return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); 213 | } 214 | 215 | /* 216 | * Convert a raw string to a hex string 217 | */ 218 | function rstr2hex(input) { 219 | var hex_tab = '0123456789abcdef', 220 | output = '', 221 | x, 222 | i; 223 | for (i = 0; i < input.length; i += 1) { 224 | x = input.charCodeAt(i); 225 | output += hex_tab.charAt((x >>> 4) & 0x0F) + 226 | hex_tab.charAt(x & 0x0F); 227 | } 228 | return output; 229 | } 230 | 231 | /* 232 | * Encode a string as utf-8 233 | */ 234 | function str2rstr_utf8(input) { 235 | return unescape(encodeURIComponent(input)); 236 | } 237 | 238 | /* 239 | * Take string arguments and return either raw or hex encoded strings 240 | */ 241 | function raw_md5(s) { 242 | return rstr_md5(str2rstr_utf8(s)); 243 | } 244 | function hex_md5(s) { 245 | return rstr2hex(raw_md5(s)); 246 | } 247 | function raw_hmac_md5(k, d) { 248 | return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)); 249 | } 250 | function hex_hmac_md5(k, d) { 251 | return rstr2hex(raw_hmac_md5(k, d)); 252 | } 253 | 254 | function md5(string, key, raw) { 255 | if (!key) { 256 | if (!raw) { 257 | return hex_md5(string); 258 | } 259 | return raw_md5(string); 260 | } 261 | if (!raw) { 262 | return hex_hmac_md5(key, string); 263 | } 264 | return raw_hmac_md5(key, string); 265 | } 266 | 267 | if (typeof define === 'function' && define.amd) { 268 | define(function () { 269 | return md5; 270 | }); 271 | } else { 272 | $.md5 = md5; 273 | } 274 | }(this)); 275 | -------------------------------------------------------------------------------- /serverDubbox/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | me.kazaff 6 | FileUploader 7 | 0.0.1 8 | jar 9 | FileUploader 10 | 11 | 12 | true 13 | 3.2.9.RELEASE 14 | 3.15.0-GA 15 | 3.2.5.Final 16 | 1.1.7 17 | 2.1.4 18 | 4.2.1 19 | 3.2.1-fixed-2 20 | 1.4.1 21 | 1.1.39 22 | 3.1 23 | 0.8 24 | 3.3.3 25 | 0.1 26 | 2.5.0 27 | 2.1.0 28 | 1.3.6 29 | 2.6.1 30 | 0.8.0 31 | 1.0.13 32 | 4.0.7 33 | 3.1.0 34 | 6.1.26 35 | 1.0.0.GA 36 | 4.2.0.Final 37 | 0.4 38 | 2.0-M5.1 39 | 3.0 40 | 2.2 41 | 3.0.8 42 | 2.3.3 43 | 1.6 44 | 8.0.11 45 | 46 | 1.6.2 47 | 1.1 48 | 1.2.16 49 | 1.0.6 50 | 51 | 4.10 52 | 3.0 53 | 0.999.8 54 | 55 | 56 | 57 | 58 | 59 | org.springframework 60 | spring-framework-bom 61 | ${spring.bom.version} 62 | pom 63 | import 64 | 65 | 66 | 67 | org.javassist 68 | javassist 69 | ${javassist_version} 70 | 71 | 72 | org.jboss.netty 73 | netty 74 | ${netty_version} 75 | 76 | 77 | org.apache.mina 78 | mina-core 79 | ${mina_version} 80 | 81 | 82 | org.glassfish.grizzly 83 | grizzly-core 84 | ${grizzly_version} 85 | 86 | 87 | org.apache.httpcomponents 88 | httpclient 89 | ${httpclient_version} 90 | 91 | 92 | com.alibaba 93 | hessian-lite 94 | ${hessian_lite_version} 95 | 96 | 97 | com.alibaba 98 | fastjson 99 | ${fastjson_version} 100 | 101 | 102 | com.thoughtworks.xstream 103 | xstream 104 | ${xstream_version} 105 | 106 | 107 | org.apache.bsf 108 | bsf-api 109 | ${bsf_version} 110 | 111 | 112 | org.jvnet.sorcerer 113 | sorcerer-javac 114 | ${sorcerer_version} 115 | 116 | 117 | org.apache.zookeeper 118 | zookeeper 119 | ${zookeeper_version} 120 | 121 | 122 | com.github.sgroschupf 123 | zkclient 124 | ${zkclient_version} 125 | 126 | 127 | org.apache.curator 128 | curator-framework 129 | ${curator_version} 130 | 131 | 132 | redis.clients 133 | jedis 134 | ${jedis_version} 135 | 136 | 137 | com.googlecode.xmemcached 138 | xmemcached 139 | ${xmemcached_version} 140 | 141 | 142 | org.apache.cxf 143 | cxf-rt-frontend-simple 144 | ${cxf_version} 145 | 146 | 147 | org.apache.cxf 148 | cxf-rt-transports-http 149 | ${cxf_version} 150 | 151 | 152 | org.apache.thrift 153 | libthrift 154 | ${thrift_version} 155 | 156 | 157 | jfree 158 | jfreechart 159 | ${jfreechart_version} 160 | 161 | 162 | com.caucho 163 | hessian 164 | ${hessian_version} 165 | 166 | 167 | javax.servlet 168 | javax.servlet-api 169 | ${servlet_version} 170 | 171 | 172 | org.mortbay.jetty 173 | jetty 174 | ${jetty_version} 175 | 176 | 177 | javax.validation 178 | validation-api 179 | ${validation_version} 180 | 181 | 182 | org.hibernate 183 | hibernate-validator 184 | ${hibernate_validator_version} 185 | 186 | 187 | javax.cache 188 | cache-api 189 | ${jcache_version} 190 | 191 | 192 | org.apache.tuscany.sca 193 | tuscany-sca-api 194 | ${sca_version} 195 | 196 | 197 | com.google.inject 198 | guice 199 | ${guice_version} 200 | 201 | 202 | com.alibaba.citrus 203 | citrus-webx-all 204 | ${webx_version} 205 | 206 | 207 | com.fasterxml.jackson.core 208 | jackson-core 209 | ${jackson_version} 210 | 211 | 212 | com.fasterxml.jackson.core 213 | jackson-databind 214 | ${jackson_version} 215 | 216 | 217 | 218 | org.slf4j 219 | slf4j-api 220 | ${slf4j_version} 221 | 222 | 223 | org.slf4j 224 | slf4j-log4j12 225 | ${slf4j_version} 226 | 227 | 228 | commons-logging 229 | commons-logging-api 230 | ${jcl_version} 231 | 232 | 233 | log4j 234 | log4j 235 | ${log4j_version} 236 | 237 | 238 | ch.qos.logback 239 | logback-classic 240 | ${logback_version} 241 | 242 | 243 | 244 | junit 245 | junit 246 | ${junit_version} 247 | test 248 | 249 | 250 | org.easymock 251 | easymock 252 | ${easymock_version} 253 | test 254 | 255 | 256 | com.googlecode.jmockit 257 | jmockit 258 | ${jmockit_version} 259 | test 260 | 261 | 262 | org.easymock 263 | easymockclassextension 264 | ${easymock_version} 265 | test 266 | 267 | 268 | cglib 269 | cglib-nodep 270 | ${cglib_version} 271 | 272 | 273 | commons-pool 274 | commons-pool 275 | ${commons_pool_version} 276 | 277 | 278 | org.apache.tomcat.embed 279 | tomcat-embed-core 280 | ${tomcat_embed_version} 281 | 282 | 283 | org.apache.tomcat.embed 284 | tomcat-embed-logging-juli 285 | ${tomcat_embed_version} 286 | 287 | 288 | 289 | 290 | 291 | 292 | org.springframework 293 | spring-core 294 | 295 | 296 | org.springframework 297 | spring-web 298 | 299 | 300 | org.springframework 301 | spring-webmvc 302 | 303 | 304 | javax.servlet 305 | javax.servlet-api 306 | provided 307 | 308 | 309 | 310 | org.javassist 311 | javassist 312 | 313 | 314 | org.jboss.netty 315 | netty 316 | 317 | 318 | org.apache.mina 319 | mina-core 320 | 321 | 322 | org.glassfish.grizzly 323 | grizzly-core 324 | 325 | 326 | org.apache.httpcomponents 327 | httpclient 328 | 329 | 330 | com.alibaba 331 | fastjson 332 | 333 | 334 | com.thoughtworks.xstream 335 | xstream 336 | 337 | 338 | org.apache.bsf 339 | bsf-api 340 | 341 | 342 | org.apache.zookeeper 343 | zookeeper 344 | 345 | 346 | com.github.sgroschupf 347 | zkclient 348 | 349 | 350 | org.apache.curator 351 | curator-framework 352 | 353 | 354 | com.googlecode.xmemcached 355 | xmemcached 356 | 357 | 358 | org.apache.cxf 359 | cxf-rt-frontend-simple 360 | 361 | 362 | org.apache.cxf 363 | cxf-rt-transports-http 364 | 365 | 366 | org.apache.thrift 367 | libthrift 368 | 369 | 370 | com.caucho 371 | hessian 372 | 373 | 374 | 375 | org.mortbay.jetty 376 | jetty 377 | 378 | 379 | org.mortbay.jetty 380 | servlet-api 381 | 382 | 383 | 384 | 385 | log4j 386 | log4j 387 | 388 | 389 | org.slf4j 390 | slf4j-api 391 | 392 | 393 | ch.qos.logback 394 | logback-classic 395 | 396 | 397 | redis.clients 398 | jedis 399 | 400 | 401 | javax.validation 402 | validation-api 403 | 404 | 405 | org.hibernate 406 | hibernate-validator 407 | 408 | 409 | javax.cache 410 | cache-api 411 | 412 | 413 | javax.ws.rs 414 | javax.ws.rs-api 415 | 2.0 416 | 417 | 418 | 419 | org.jboss.resteasy 420 | resteasy-jaxrs 421 | 3.0.7.Final 422 | 423 | 424 | org.jboss.resteasy 425 | resteasy-client 426 | 3.0.7.Final 427 | 428 | 429 | 430 | org.jboss.resteasy 431 | resteasy-jaxb-provider 432 | 3.0.7.Final 433 | 434 | 435 | 436 | org.jboss.resteasy 437 | resteasy-multipart-provider 438 | 3.0.7.Final 439 | 440 | 441 | 442 | commons-io 443 | commons-io 444 | 2.4 445 | 446 | 447 | 448 | javax.validation 449 | validation-api 450 | 1.0.0.GA 451 | 452 | 453 | 454 | 455 | org.jboss.resteasy 456 | resteasy-jackson-provider 457 | 3.0.7.Final 458 | 459 | 460 | 461 | 462 | org.jboss.resteasy 463 | resteasy-jaxb-provider 464 | 3.0.7.Final 465 | 466 | 467 | 468 | 469 | org.apache.tomcat.embed 470 | tomcat-embed-core 471 | 472 | 473 | org.apache.tomcat.embed 474 | tomcat-embed-logging-juli 475 | 476 | 477 | 478 | 479 | com.esotericsoftware.kryo 480 | kryo 481 | 2.24.0 482 | 483 | 484 | de.javakaffee 485 | kryo-serializers 486 | 0.26 487 | 488 | 489 | de.ruedigermoeller 490 | fst 491 | 1.55 492 | 493 | 494 | com.fasterxml.jackson.core 495 | jackson-core 496 | 2.3.3 497 | 498 | 499 | com.fasterxml.jackson.core 500 | jackson-databind 501 | 2.3.3 502 | 503 | 504 | 505 | 506 | -------------------------------------------------------------------------------- /serverDubbox/resources/META-INF/spring/application.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | classpath:/configure.properties 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /serverDubbox/resources/META-INF/spring/provider.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /serverDubbox/resources/configure.properties: -------------------------------------------------------------------------------- 1 | upload.folder=D:/code/test/webuploaderDemo/uploads -------------------------------------------------------------------------------- /serverDubbox/resources/dubbo.properties: -------------------------------------------------------------------------------- 1 | dubbo.container=log4j,spring -------------------------------------------------------------------------------- /serverDubbox/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/Main.java: -------------------------------------------------------------------------------- 1 | package me.kazaff; 2 | 3 | /** 4 | * Created by kazaff on 2015/3/18. 5 | */ 6 | public class Main { 7 | 8 | public static void main(String[] args) { 9 | com.alibaba.dubbo.container.Main.main(args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/wu/controller/FileUploadeService.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.controller; 2 | 3 | import me.kazaff.wu.entity.FileInfo; 4 | import me.kazaff.wu.entity.FileUploadForm; 5 | import me.kazaff.wu.entity.Result; 6 | import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; 7 | 8 | 9 | /** 10 | * Created by kazaff on 2015/3/18. 11 | */ 12 | public interface FileUploadeService { 13 | public Result check(String status, FileInfo info); 14 | public Result fileUpload(MultipartFormDataInput form); 15 | //public Result fileUpload(FileUploadForm form); 16 | } 17 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/wu/controller/FileUploader.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.controller; 2 | 3 | import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType; 4 | import me.kazaff.wu.entity.FileInfo; 5 | import me.kazaff.wu.entity.FileUploadForm; 6 | import me.kazaff.wu.entity.Result; 7 | import me.kazaff.wu.service.webUploader; 8 | import org.apache.commons.io.IOUtils; 9 | import org.jboss.resteasy.annotations.Form; 10 | import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; 11 | import org.jboss.resteasy.plugins.providers.multipart.InputPart; 12 | import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.beans.factory.annotation.Value; 17 | 18 | import javax.ws.rs.*; 19 | import javax.ws.rs.core.MediaType; 20 | import java.io.File; 21 | import java.io.FileOutputStream; 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | /** 28 | * Created by kazaff on 2015/3/18. 29 | */ 30 | 31 | @Path("/") 32 | public class FileUploader implements FileUploadeService { 33 | 34 | private final static Logger log = LoggerFactory.getLogger(FileUploader.class); 35 | 36 | @Value("${upload.folder}") 37 | private String uploadFolder; 38 | 39 | @Autowired 40 | private webUploader wu; 41 | 42 | @POST 43 | @Path("fileUpload") 44 | @Consumes(MediaType.APPLICATION_FORM_URLENCODED+";charset=UTF-8") 45 | @Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8}) 46 | public Result check(@FormParam("status") @DefaultValue("") String status, @Form FileInfo info){ 47 | 48 | Result rs = new Result(); 49 | 50 | //秒传验证 51 | if("md5Check".equals(status)){ 52 | String path = wu.md5Check(info.getMd5()); 53 | if(path == null || path.isEmpty()){ 54 | rs.setIfExist(false); 55 | }else{ 56 | rs.setIfExist(true); 57 | rs.setPath(path); 58 | } 59 | 60 | }else if("chunkCheck".equals(status)){ //分块验证 61 | 62 | //检查目标分片是否存在且完整 63 | if(wu.chunkCheck(this.uploadFolder + "/" + info.getName() + "/" + info.getChunkIndex(), Long.valueOf(info.getSize()))){ 64 | rs.setIfExist(true); 65 | }else{ 66 | rs.setIfExist(false); 67 | } 68 | 69 | }else if("chunksMerge".equals(status)){ //分块合并 70 | 71 | String path = wu.chunksMerge(info.getName(), info.getExt(), info.getChunks(), info.getMd5(), this.uploadFolder); 72 | if(path == null){ 73 | rs.setStatus(0); 74 | rs.setMessage(wu.getErrorMsg()); 75 | }else{ 76 | rs.setStatus(1); 77 | rs.setPath(path); 78 | } 79 | 80 | }else{ 81 | log.error("请求参数不完整"); 82 | rs.setStatus(0); 83 | rs.setMessage("请求参数不完整"); 84 | } 85 | 86 | return rs; 87 | } 88 | 89 | @POST 90 | @Path("fileUpload") 91 | @Consumes(MediaType.MULTIPART_FORM_DATA) 92 | @Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8}) 93 | public Result fileUpload(MultipartFormDataInput formDataInput){ 94 | 95 | //转换成实体对象 96 | FileUploadForm form = new FileUploadForm(); 97 | Map> uploadForm = formDataInput.getFormDataMap(); 98 | 99 | try{ 100 | //name 101 | InputPart inputParts = uploadForm.get("name").get(0); 102 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 103 | form.setName(inputParts.getBodyAsString()); 104 | 105 | //lastModifiedDate 106 | inputParts = uploadForm.get("lastModifiedDate").get(0); 107 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 108 | form.setLastModifiedDate(inputParts.getBodyAsString()); 109 | 110 | //userId 111 | inputParts = uploadForm.get("userId").get(0); 112 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 113 | form.setUserId(inputParts.getBodyAsString()); 114 | 115 | //md5 116 | inputParts = uploadForm.get("md5").get(0); 117 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 118 | form.setMd5(inputParts.getBodyAsString()); 119 | 120 | //id 121 | inputParts = uploadForm.get("id").get(0); 122 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 123 | form.setId(inputParts.getBodyAsString()); 124 | 125 | //type 126 | inputParts = uploadForm.get("type").get(0); 127 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 128 | form.setType(inputParts.getBodyAsString()); 129 | 130 | //size 131 | inputParts = uploadForm.get("size").get(0); 132 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 133 | form.setSize(inputParts.getBodyAsString()); 134 | 135 | //chunks 136 | inputParts = uploadForm.get("chunks").get(0); 137 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 138 | form.setChunks(Integer.valueOf(inputParts.getBodyAsString())); 139 | 140 | //chunk 141 | inputParts = uploadForm.get("chunk").get(0); 142 | inputParts.setMediaType(MediaType.TEXT_PLAIN_TYPE); 143 | form.setChunk(Integer.valueOf(inputParts.getBodyAsString())); 144 | 145 | //file 146 | inputParts = uploadForm.get("file").get(0); 147 | InputStream inputStream = inputParts.getBody(InputStream.class, null); 148 | form.setFileData(IOUtils.toByteArray(inputStream)); 149 | 150 | 151 | }catch (IOException e) { 152 | log.error("异常: " + e); 153 | return new Result(0, "数据上传失败"); 154 | } 155 | 156 | System.out.println(form); 157 | 158 | return handleUpload(form); 159 | } 160 | 161 | /* 162 | //由于能力有限,不知道如何在使用@MultipartForm时,解决中文乱码问题 163 | @POST 164 | @Path("fileUpload") 165 | @Consumes(MediaType.MULTIPART_FORM_DATA+";charset=UTF-8") 166 | @Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8}) 167 | public Result fileUpload(@MultipartForm FileUploadForm form){ 168 | return handleUpload(form); 169 | }*/ 170 | 171 | private Result handleUpload(FileUploadForm form){ 172 | try{ 173 | File target = wu.getReadySpace(form, this.uploadFolder); 174 | if(target == null) { 175 | return new Result(0, wu.getErrorMsg()); 176 | } 177 | 178 | //todo 可以使用buffer或nio通道来优化文件读写 179 | FileOutputStream out = new FileOutputStream(target); 180 | out.write(form.getFileData()); 181 | out.flush(); 182 | out.close(); 183 | 184 | //将MD5签名和合并后的文件path存入持久层,注意这里这个需求导致需要修改webuploader.js源码3170行 185 | //因为原始webuploader.js不支持为formData设置函数类型参数,这将导致不能在控件初始化后修改该参数 186 | if(form.getChunks() <= 0){ 187 | if(!wu.saveMd52FileMap(form.getMd5(), target.getName())){ 188 | log.error("文件[" + form.getMd5() + "=>" + target.getName() + "]保存关系到持久成失败,但并不影响文件上传,只会导致日后该文件可能被重复上传而已"); 189 | } 190 | } 191 | 192 | Result rs = new Result(); 193 | rs.setStatus(1); 194 | rs.setPath(target.getName()); 195 | return rs; 196 | 197 | }catch (IOException ex){ 198 | log.error("数据上传失败", ex); 199 | return new Result(0, "数据上传失败"); 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/wu/entity/FileInfo.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.entity; 2 | 3 | import javax.ws.rs.DefaultValue; 4 | import javax.ws.rs.FormParam; 5 | 6 | /** 7 | * Created by kazaff on 2014/12/1. 8 | */ 9 | public class FileInfo { 10 | 11 | @FormParam("md5") 12 | @DefaultValue("") 13 | private String md5; 14 | 15 | @FormParam("chunkIndex") 16 | @DefaultValue("-1") 17 | private int chunkIndex; 18 | 19 | @FormParam("size") 20 | @DefaultValue("-1") 21 | private String size; 22 | 23 | @FormParam("name") 24 | @DefaultValue("") 25 | private String name; 26 | 27 | 28 | @FormParam("chunks") 29 | @DefaultValue("-1") 30 | private int chunks; 31 | 32 | @FormParam("ext") 33 | @DefaultValue("") 34 | private String ext; 35 | 36 | public FileInfo(){} 37 | 38 | public int getChunks() { 39 | return chunks; 40 | } 41 | 42 | public void setChunks(int chunks) { 43 | this.chunks = chunks; 44 | } 45 | 46 | public String getMd5() { 47 | return md5; 48 | } 49 | 50 | public void setMd5(String md5) { 51 | this.md5 = md5; 52 | } 53 | 54 | public int getChunkIndex() { 55 | return chunkIndex; 56 | } 57 | 58 | public void setChunkIndex(int chunkIndex) { 59 | this.chunkIndex = chunkIndex; 60 | } 61 | 62 | public String getSize() { 63 | return size; 64 | } 65 | 66 | public void setSize(String size) { 67 | this.size = size; 68 | } 69 | 70 | public String getName() { 71 | return name; 72 | } 73 | 74 | public void setName(String name) { 75 | this.name = name; 76 | } 77 | 78 | public String getExt() { 79 | return ext; 80 | } 81 | 82 | public void setExt(String ext) { 83 | this.ext = ext; 84 | } 85 | 86 | public String toString(){ 87 | return "name=" + this.name + "; size=" + this.size + "; chunkIndex=" + this.chunkIndex + "; md5=" + this.md5 88 | + "; chunks=" + this.chunks + "; ext=" + this.ext; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/wu/entity/FileUploadForm.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.entity; 2 | 3 | import org.jboss.resteasy.annotations.providers.multipart.PartType; 4 | 5 | import javax.ws.rs.Consumes; 6 | import javax.ws.rs.DefaultValue; 7 | import javax.ws.rs.Encoded; 8 | import javax.ws.rs.FormParam; 9 | import javax.ws.rs.core.MediaType; 10 | import java.util.Arrays; 11 | 12 | /** 13 | * Created by kazaff on 2015/3/18. 14 | */ 15 | public class FileUploadForm { 16 | 17 | private byte[] fileData; 18 | private String userId; 19 | private String md5; 20 | private String id; 21 | private String name; 22 | private String type; 23 | private String lastModifiedDate; 24 | private String size; 25 | private int chunks; 26 | private int chunk; 27 | 28 | public int getChunks() { 29 | return chunks; 30 | } 31 | 32 | @FormParam("chunks") 33 | @DefaultValue("-1") 34 | public void setChunks(int chunks) { 35 | this.chunks = chunks; 36 | } 37 | 38 | public int getChunk() { 39 | return chunk; 40 | } 41 | 42 | @FormParam("chunk") 43 | @DefaultValue("-1") 44 | public void setChunk(int chunk) { 45 | this.chunk = chunk; 46 | } 47 | 48 | public String getUserId() { 49 | return userId; 50 | } 51 | 52 | @FormParam("userId") 53 | @DefaultValue("-1") 54 | public void setUserId(String userId) { 55 | this.userId = userId; 56 | } 57 | 58 | public String getMd5() { 59 | return md5; 60 | } 61 | 62 | @FormParam("md5") 63 | @DefaultValue("-1") 64 | public void setMd5(String md5) { 65 | this.md5 = md5; 66 | } 67 | 68 | public String getId() { 69 | return id; 70 | } 71 | 72 | @FormParam("id") 73 | @DefaultValue("-1") 74 | public void setId(String id) { 75 | this.id = id; 76 | } 77 | 78 | public String getName() { 79 | return name; 80 | } 81 | 82 | @FormParam("name") 83 | @DefaultValue("") 84 | public void setName(String name) { 85 | this.name = name; 86 | } 87 | 88 | public String getType() { 89 | return type; 90 | } 91 | 92 | @FormParam("type") 93 | @DefaultValue("") 94 | public void setType(String type) { 95 | this.type = type; 96 | } 97 | 98 | public String getLastModifiedDate() { 99 | return lastModifiedDate; 100 | } 101 | 102 | @FormParam("lastModifiedDate") 103 | @DefaultValue("") 104 | public void setLastModifiedDate(String lastModifiedDate) { 105 | this.lastModifiedDate = lastModifiedDate; 106 | } 107 | 108 | public String getSize() { 109 | return size; 110 | } 111 | 112 | @FormParam("size") 113 | @DefaultValue("-1") 114 | public void setSize(String size) { 115 | this.size = size; 116 | } 117 | 118 | public byte[] getFileData() { 119 | return fileData; 120 | } 121 | 122 | @FormParam("file") 123 | @DefaultValue("") 124 | @PartType("application/octet-stream") 125 | public void setFileData(byte[] fileData) { 126 | this.fileData = fileData; 127 | } 128 | 129 | @Override 130 | public String toString() { 131 | return "FileUploadForm{" + 132 | ", userId='" + userId + '\'' + 133 | ", md5='" + md5 + '\'' + 134 | ", id='" + id + '\'' + 135 | ", name='" + name + '\'' + 136 | ", type='" + type + '\'' + 137 | ", lastModifiedDate='" + lastModifiedDate + '\'' + 138 | ", size='" + size + '\'' + 139 | ", chunks=" + chunks + 140 | ", chunk=" + chunk + 141 | '}'; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/wu/entity/Result.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.entity; 2 | 3 | import javax.xml.bind.annotation.XmlRootElement; 4 | 5 | /** 6 | * Created by kazaff on 2015/3/18. 7 | */ 8 | @XmlRootElement 9 | public class Result { 10 | 11 | private int status; 12 | private String message; 13 | private String path; 14 | private boolean ifExist; 15 | 16 | public Result() { 17 | } 18 | 19 | public Result(int status, String message) { 20 | this.status = status; 21 | this.message = message; 22 | } 23 | 24 | public Result(int status, String message, String path, boolean ifExist) { 25 | this.status = status; 26 | this.message = message; 27 | this.path = path; 28 | this.ifExist = ifExist; 29 | } 30 | 31 | public int getStatus() { 32 | return status; 33 | } 34 | 35 | public void setStatus(int status) { 36 | this.status = status; 37 | } 38 | 39 | public String getMessage() { 40 | return message; 41 | } 42 | 43 | public void setMessage(String message) { 44 | this.message = message; 45 | } 46 | 47 | public String getPath() { 48 | return path; 49 | } 50 | 51 | public void setPath(String path) { 52 | this.path = path; 53 | } 54 | 55 | public boolean isIfExist() { 56 | return ifExist; 57 | } 58 | 59 | public void setIfExist(boolean ifExist) { 60 | this.ifExist = ifExist; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/wu/extension/CORSFilter.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.extension; 2 | 3 | import javax.ws.rs.container.ContainerRequestContext; 4 | import javax.ws.rs.container.ContainerResponseContext; 5 | import javax.ws.rs.container.ContainerResponseFilter; 6 | 7 | /** 8 | * Created by kazaff on 2015/3/18. 9 | */ 10 | public class CORSFilter implements ContainerResponseFilter { 11 | public void filter(ContainerRequestContext request, ContainerResponseContext response){ 12 | 13 | response.getHeaders().add("Access-Control-Allow-Origin", "*"); 14 | 15 | if(request.getMethod().equals("OPTIONS")){ 16 | response.getHeaders().add("Access-Control-Allow-Methods", "HEAD,GET,POST,PUT,DELETE,OPTIONS"); 17 | response.getHeaders().add("Access-Control-Allow-Headers", "Content-Type,Origin,Accept"); 18 | response.getHeaders().add("Access-Control-Max-Age", "120"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/wu/service/webUploader.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.service; 2 | 3 | import me.kazaff.wu.entity.FileInfo; 4 | import me.kazaff.wu.entity.FileUploadForm; 5 | import me.kazaff.wu.util.fileLock; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.context.annotation.Scope; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.io.*; 12 | import java.nio.channels.FileChannel; 13 | import java.security.MessageDigest; 14 | import java.security.NoSuchAlgorithmException; 15 | import java.util.*; 16 | import java.util.concurrent.locks.Lock; 17 | 18 | /** 19 | * Created by kazaff on 2014/12/2. 20 | */ 21 | 22 | @Service 23 | @Scope("prototype") 24 | public class webUploader { 25 | 26 | private final static Logger log = LoggerFactory.getLogger(webUploader.class); 27 | 28 | /** 29 | * 错误详情 30 | */ 31 | private String msg; 32 | 33 | /** 34 | * 秒传验证 35 | * 根据文件的MD5签名判断该文件是否已经存在 36 | * 37 | * @param key 文件的md5签名 38 | * @return 若存在则返回该文件的路径,不存在则返回null 39 | */ 40 | public String md5Check(String key){ 41 | 42 | //todo 模拟去数据库查找 43 | Map data = new HashMap(); 44 | data.put("b0201e4d41b2eeefc7d3d355a44c6f5a", "kazaff2.jpg"); 45 | 46 | if(data.containsKey(key)){ 47 | return data.get(key); 48 | }else{ 49 | return null; 50 | } 51 | } 52 | 53 | /** 54 | * 分片验证 55 | * 验证对应分片文件是否存在,大小是否吻合 56 | * @param file 分片文件的路径 57 | * @param size 分片文件的大小 58 | * @return 59 | */ 60 | public boolean chunkCheck(String file, Long size){ 61 | //检查目标分片是否存在且完整 62 | File target = new File(file); 63 | if(target.isFile() && size == target.length()){ 64 | return true; 65 | }else{ 66 | return false; 67 | } 68 | } 69 | 70 | /** 71 | * 分片合并操作 72 | * 要点: 73 | * > 合并: NIO 74 | * > 并发锁: 避免多线程同时触发合并操作 75 | * > 清理: 合并清理不再需要的分片文件、文件夹、tmp文件 76 | * @param folder 分片文件所在的文件夹名称 77 | * @param ext 合并后的文件后缀名 78 | * @param chunks 分片总数 79 | * @param md5 文件签名 80 | * @param path 合并后的文件所存储的位置 81 | * @return 82 | */ 83 | public String chunksMerge(String folder, String ext, int chunks, String md5, String path){ 84 | 85 | //合并后的目标文件 86 | String target; 87 | 88 | //检查是否满足合并条件:分片数量是否足够 89 | if(chunks == this.getChunksNum(path + "/" + folder)){ 90 | 91 | //同步指定合并的对象 92 | Lock lock = fileLock.getLock(folder); 93 | lock.lock(); 94 | try{ 95 | //检查是否满足合并条件:分片数量是否足够 96 | //File[] files = this.getChunks(path + "/" +folder); 97 | List files = new ArrayList(Arrays.asList(this.getChunks(path + "/" +folder))); 98 | if(chunks == files.size()){ 99 | 100 | //按照名称排序文件,这里分片都是按照数字命名的 101 | Collections.sort(files, new Comparator() { 102 | @Override 103 | public int compare(File o1, File o2) { 104 | if(Integer.valueOf(o1.getName()) < Integer.valueOf(o2.getName())){ 105 | return -1; 106 | } 107 | return 1; 108 | } 109 | }); 110 | 111 | //创建合并后的文件 112 | File outputFile = new File(path + "/" + this.randomFileName(ext)); 113 | if(outputFile.exists()){ 114 | log.error("文件[" + folder + "]随机命名冲突"); 115 | this.setErrorMsg("文件随机命名冲突"); 116 | return null; 117 | } 118 | outputFile.createNewFile(); 119 | FileChannel outChannel = new FileOutputStream(outputFile).getChannel(); 120 | 121 | //合并 122 | FileChannel inChannel; 123 | for(File file : files){ 124 | inChannel = new FileInputStream(file).getChannel(); 125 | inChannel.transferTo(0, inChannel.size(), outChannel); 126 | inChannel.close(); 127 | 128 | //删除分片 129 | if(!file.delete()){ 130 | log.error("分片[" + folder + "=>" + file.getName() + "]删除失败"); 131 | } 132 | } 133 | outChannel.close(); 134 | files = null; 135 | 136 | //将MD5签名和合并后的文件path存入持久层 137 | if(this.saveMd52FileMap(md5, outputFile.getName())){ 138 | log.error("文件[" + md5 + "=>" + outputFile.getName() + "]保存关系到持久成失败,但并不影响文件上传,只会导致日后该文件可能被重复上传而已"); 139 | } 140 | 141 | //清理:文件夹,tmp文件 142 | this.cleanSpace(folder, path); 143 | 144 | return outputFile.getName(); 145 | } 146 | }catch(Exception ex){ 147 | log.error("数据分片合并失败", ex); 148 | this.setErrorMsg("数据分片合并失败"); 149 | return null; 150 | 151 | }finally { 152 | //解锁 153 | lock.unlock(); 154 | //清理锁对象 155 | fileLock.removeLock(folder); 156 | } 157 | } 158 | 159 | //去持久层查找对应md5签名,直接返回对应path 160 | target = this.md5Check(md5); 161 | if(target == null){ 162 | log.error("文件[签名:" + md5 + "]数据不完整,可能该文件正在合并中"); 163 | this.setErrorMsg("数据不完整,可能该文件正在合并中"); 164 | return null; 165 | } 166 | 167 | return target; 168 | } 169 | 170 | /** 171 | * 将MD5签名和目标文件path的映射关系存入持久层 172 | * @param key md5签名 173 | * @param file 文件路径 174 | * @return 175 | */ 176 | public boolean saveMd52FileMap(String key, String file){ 177 | //todo 178 | return true; 179 | } 180 | 181 | /** 182 | * 为上传的文件创建对应的保存位置 183 | * 若上传的是分片,则会创建对应的文件夹结构和tmp文件 184 | * @param info 上传文件的相关信息 185 | * @param path 文件保存根路径 186 | * @return 187 | */ 188 | public File getReadySpace(FileUploadForm info, String path){ 189 | 190 | //创建上传文件所需的文件夹 191 | if(!this.createFileFolder(path, false)){ 192 | return null; 193 | } 194 | 195 | String newFileName; //上传文件的新名称 196 | 197 | //如果是分片上传,则需要为分片创建文件夹 198 | if (info.getChunks() > 0) { 199 | newFileName = String.valueOf(info.getChunk()); 200 | 201 | String fileFolder = this.md5(info.getUserId() + info.getName() + info.getType() + info.getLastModifiedDate() + info.getSize()); 202 | if(fileFolder == null){ 203 | return null; 204 | } 205 | 206 | path += "/" + fileFolder; //文件上传路径更新为指定文件信息签名后的临时文件夹,用于后期合并 207 | 208 | if(!this.createFileFolder(path, true)){ 209 | return null; 210 | } 211 | 212 | } else { 213 | //生成随机文件名 214 | newFileName = this.randomFileName(info.getName()); 215 | } 216 | 217 | return new File(path, newFileName); 218 | } 219 | 220 | /** 221 | * 清理分片上传的相关数据 222 | * 文件夹,tmp文件 223 | * @param folder 文件夹名称 224 | * @param path 上传文件根路径 225 | * @return 226 | */ 227 | private boolean cleanSpace(String folder, String path){ 228 | //删除分片文件夹 229 | File garbage = new File(path + "/" + folder); 230 | if(!garbage.delete()){ 231 | return false; 232 | } 233 | 234 | //删除tmp文件 235 | garbage = new File(path + "/" + folder + ".tmp"); 236 | if(!garbage.delete()){ 237 | return false; 238 | } 239 | 240 | return true; 241 | } 242 | 243 | /** 244 | * 获取指定文件的所有分片 245 | * @param folder 文件夹路径 246 | * @return 247 | */ 248 | private File[] getChunks(String folder){ 249 | File targetFolder = new File(folder); 250 | return targetFolder.listFiles(new FileFilter() { 251 | @Override 252 | public boolean accept(File file) { 253 | if (file.isDirectory()) { 254 | return false; 255 | } 256 | return true; 257 | } 258 | }); 259 | } 260 | 261 | /** 262 | * 获取指定文件的分片数量 263 | * @param folder 文件夹路径 264 | * @return 265 | */ 266 | private int getChunksNum(String folder){ 267 | 268 | File[] filesList = this.getChunks(folder); 269 | if(filesList == null){ 270 | return 0; 271 | } 272 | return filesList.length; 273 | } 274 | 275 | /** 276 | * 创建存放上传的文件的文件夹 277 | * @param file 文件夹路径 278 | * @return 279 | */ 280 | private boolean createFileFolder(String file, boolean hasTmp){ 281 | 282 | //创建存放分片文件的临时文件夹 283 | File tmpFile = new File(file); 284 | if(!tmpFile.exists()){ 285 | try { 286 | tmpFile.mkdir(); 287 | }catch(SecurityException ex){ 288 | log.error("无法创建文件夹", ex); 289 | this.setErrorMsg("无法创建文件夹"); 290 | return false; 291 | } 292 | } 293 | 294 | if(hasTmp){ 295 | //创建一个对应的文件,用来记录上传分片文件的修改时间,用于清理长期未完成的垃圾分片 296 | tmpFile = new File(file + ".tmp"); 297 | if(tmpFile.exists()){ 298 | tmpFile.setLastModified(System.currentTimeMillis()); 299 | }else{ 300 | try{ 301 | tmpFile.createNewFile(); 302 | }catch(IOException ex){ 303 | log.error("无法创建tmp文件", ex); 304 | this.setErrorMsg("无法创建tmp文件"); 305 | return false; 306 | } 307 | } 308 | } 309 | 310 | return true; 311 | } 312 | 313 | /** 314 | * 为上传的文件生成随机名称 315 | * @param originalName 文件的原始名称,主要用来获取文件的后缀名 316 | * @return 317 | */ 318 | private String randomFileName(String originalName){ 319 | String ext[] = originalName.split("\\."); 320 | return UUID.randomUUID().toString() + "." + ext[ext.length-1]; 321 | } 322 | 323 | /** 324 | * MD5签名 325 | * @param content 要签名的内容 326 | * @return 327 | */ 328 | private String md5(String content){ 329 | StringBuffer sb = new StringBuffer(); 330 | try{ 331 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 332 | md5.update(content.getBytes("UTF-8")); 333 | byte[] tmpFolder = md5.digest(); 334 | 335 | for(int i = 0; i < tmpFolder.length; i++){ 336 | sb.append(Integer.toString((tmpFolder[i] & 0xff) + 0x100, 16).substring(1)); 337 | } 338 | 339 | return sb.toString(); 340 | }catch(NoSuchAlgorithmException ex){ 341 | log.error("无法生成文件的MD5签名", ex); 342 | this.setErrorMsg("无法生成文件的MD5签名"); 343 | return null; 344 | }catch(UnsupportedEncodingException ex){ 345 | log.error("无法生成文件的MD5签名", ex); 346 | this.setErrorMsg("无法生成文件的MD5签名"); 347 | return null; 348 | } 349 | } 350 | 351 | /** 352 | * 记录异常错误信息 353 | * @param msg 错误详细 354 | */ 355 | private void setErrorMsg(String msg){ 356 | this.msg = msg; 357 | } 358 | 359 | /** 360 | * 获取错误详细 361 | * @return 362 | */ 363 | public String getErrorMsg(){ 364 | return this.msg; 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /serverDubbox/src/main/java/me/kazaff/wu/util/fileLock.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.util; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.concurrent.locks.Lock; 8 | import java.util.concurrent.locks.ReentrantLock; 9 | 10 | /** 11 | * Created by kazaff on 2014/12/2. 12 | */ 13 | @Component 14 | public class fileLock { 15 | 16 | private static Map LOCKS = new HashMap(); 17 | 18 | public static synchronized Lock getLock(String key){ 19 | if(LOCKS.containsKey(key)){ 20 | return LOCKS.get(key); 21 | }else{ 22 | Lock one = new ReentrantLock(); 23 | LOCKS.put(key, one); 24 | return one; 25 | } 26 | } 27 | 28 | public static synchronized void removeLock(String key){ 29 | LOCKS.remove(key); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /serverJAVAEE/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.springapp 5 | webuploader 6 | war 7 | 1.0-SNAPSHOT 8 | webuploader 9 | 10 | 11 | 4.1.1.RELEASE 12 | 13 | 14 | 15 | 16 | org.springframework 17 | spring-core 18 | ${spring.version} 19 | 20 | 21 | 22 | org.springframework 23 | spring-web 24 | ${spring.version} 25 | 26 | 27 | 28 | javax.servlet 29 | servlet-api 30 | 2.5 31 | 32 | 33 | 34 | javax.servlet.jsp 35 | jsp-api 36 | 2.1 37 | provided 38 | 39 | 40 | 41 | org.springframework 42 | spring-webmvc 43 | ${spring.version} 44 | 45 | 46 | 47 | org.springframework 48 | spring-test 49 | ${spring.version} 50 | test 51 | 52 | 53 | 54 | commons-fileupload 55 | commons-fileupload 56 | 1.3.1 57 | 58 | 59 | 60 | commons-io 61 | commons-io 62 | 2.4 63 | 64 | 65 | 66 | ch.qos.logback 67 | logback-classic 68 | 1.1.2 69 | 70 | 71 | 72 | org.logback-extensions 73 | logback-ext-spring 74 | 0.1.2 75 | 76 | 77 | 78 | junit 79 | junit 80 | 4.11 81 | test 82 | 83 | 84 | 85 | 86 | webuploader 87 | 88 | 89 | maven-compiler-plugin 90 | 91 | 1.6 92 | 1.6 93 | 94 | 95 | 96 | maven-surefire-plugin 97 | 98 | 99 | **/*Tests.java 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /serverJAVAEE/src/main/java/me/kazaff/filters/CORSFilter.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.filters; 2 | 3 | import org.apache.commons.logging.LogFactory; 4 | import org.springframework.web.filter.OncePerRequestFilter; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | 12 | /** 13 | * Created by kazaff on 2014/12/1. 14 | */ 15 | public class CORSFilter extends OncePerRequestFilter{ 16 | 17 | //private static final Log LOG = LogFactory.getLog(CORSFilter.class); 18 | 19 | @Override 20 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 21 | response.addHeader("Access-Control-Allow-Origin", "*"); 22 | 23 | if(request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) { 24 | response.addHeader("Access-Control-Allow-Methods", "HEAD,GET,POST,PUT,DELETE,OPTIONS"); 25 | response.addHeader("Access-Control-Allow-Headers", "Content-Type,Origin,Accept"); 26 | response.addHeader("Access-Control-Max-Age", "120"); 27 | } 28 | 29 | filterChain.doFilter(request, response); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /serverJAVAEE/src/main/java/me/kazaff/wu/controller/FileUploadController.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.controller; 2 | 3 | import me.kazaff.wu.entity.FileInfo; 4 | import me.kazaff.wu.service.webUploader; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.ui.ModelMap; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | import org.springframework.web.bind.annotation.ResponseBody; 16 | import org.springframework.web.multipart.MultipartFile; 17 | 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.io.*; 20 | 21 | @Controller 22 | @RequestMapping("/") 23 | public class FileUploadController { 24 | 25 | private final static Logger log = LoggerFactory.getLogger(FileUploadController.class); 26 | 27 | @Value("${upload.folder}") 28 | private String uploadFolder; 29 | 30 | @Autowired 31 | private webUploader wu; 32 | 33 | @RequestMapping(method = RequestMethod.GET) 34 | public String printWelcome(ModelMap model) { 35 | model.addAttribute("message", "Hello world!"); 36 | return "hello"; 37 | } 38 | 39 | //大文件上传 40 | @RequestMapping(value = "fileUpload", method = RequestMethod.POST) 41 | @ResponseBody 42 | public String fileUpload(String status, FileInfo info, @RequestParam(value = "file", required = false) MultipartFile file){ 43 | 44 | if(status == null){ //文件上传 45 | if(file != null && !file.isEmpty()){ //验证请求不会包含数据上传,所以避免NullPoint这里要检查一下file变量是否为null 46 | try { 47 | File target = wu.getReadySpace(info, this.uploadFolder); //为上传的文件准备好对应的位置 48 | if(target == null){ 49 | return "{\"status\": 0, \"message\": \"" + wu.getErrorMsg() + "\"}"; 50 | } 51 | 52 | file.transferTo(target); //保存上传文件 53 | 54 | //将MD5签名和合并后的文件path存入持久层,注意这里这个需求导致需要修改webuploader.js源码3170行 55 | //因为原始webuploader.js不支持为formData设置函数类型参数,这将导致不能在控件初始化后修改该参数 56 | if(info.getChunks() <= 0){ 57 | if(!wu.saveMd52FileMap(info.getMd5(), target.getName())){ 58 | log.error("文件[" + info.getMd5() + "=>" + target.getName() + "]保存关系到持久成失败,但并不影响文件上传,只会导致日后该文件可能被重复上传而已"); 59 | } 60 | } 61 | 62 | return "{\"status\": 1, \"path\": \"" + target.getName() + "\"}"; 63 | 64 | }catch(IOException ex){ 65 | log.error("数据上传失败", ex); 66 | return "{\"status\": 0, \"message\": \"数据上传失败\"}"; 67 | } 68 | } 69 | }else{ 70 | if(status.equals("md5Check")){ //秒传验证 71 | 72 | String path = wu.md5Check(info.getMd5()); 73 | 74 | if(path == null){ 75 | return "{\"ifExist\": 0}"; 76 | }else{ 77 | return "{\"ifExist\": 1, \"path\": \"" + path + "\"}"; 78 | } 79 | 80 | }else if(status.equals("chunkCheck")){ //分块验证 81 | 82 | //检查目标分片是否存在且完整 83 | if(wu.chunkCheck(this.uploadFolder + "/" + info.getName() + "/" + info.getChunkIndex(), Long.valueOf(info.getSize()))){ 84 | return "{\"ifExist\": 1}"; 85 | }else{ 86 | return "{\"ifExist\": 0}"; 87 | } 88 | 89 | }else if(status.equals("chunksMerge")){ //分块合并 90 | 91 | String path = wu.chunksMerge(info.getName(), info.getExt(), info.getChunks(), info.getMd5(), this.uploadFolder); 92 | if(path == null){ 93 | return "{\"status\": 0, \"message\": \"" + wu.getErrorMsg() + "\"}"; 94 | } 95 | 96 | return "{\"status\": 1, \"path\": \"" + path + "\", \"message\": \"中文测试\"}"; 97 | } 98 | } 99 | 100 | log.error("请求参数不完整"); 101 | return "{\"status\": 0, \"message\": \"请求参数不完整\"}"; 102 | } 103 | } -------------------------------------------------------------------------------- /serverJAVAEE/src/main/java/me/kazaff/wu/entity/FileInfo.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.entity; 2 | 3 | /** 4 | * Created by kazaff on 2014/12/1. 5 | */ 6 | public class FileInfo { 7 | 8 | private String md5; 9 | private int chunkIndex; 10 | private String size; 11 | private String name; 12 | private String userId; 13 | private String id; 14 | private int chunks; 15 | private int chunk; 16 | private String lastModifiedDate; 17 | private String type; 18 | private String ext; 19 | 20 | public FileInfo(){} 21 | 22 | public FileInfo(String name, String size, int chunkIndex){ 23 | this.name = name; 24 | this.size = size; 25 | this.chunkIndex = chunkIndex; 26 | } 27 | 28 | public FileInfo(String userId, String id){ 29 | this.userId = userId; 30 | this.id = id; 31 | } 32 | 33 | public FileInfo(String md5){ 34 | this.md5 = md5; 35 | } 36 | 37 | public FileInfo(int chunks, int chunk, String userId, String id, String name, String size, String lastModifiedDate, String type){ 38 | this.userId = userId; 39 | this.id = id; 40 | this.name = name; 41 | this.size = size; 42 | this.chunks = chunks; 43 | this.chunk = chunk; 44 | this.lastModifiedDate = lastModifiedDate; 45 | this.type = type; 46 | } 47 | 48 | public FileInfo(String name, int chunks, String ext, String md5){ 49 | this.name = name; 50 | this.chunks = chunks; 51 | this.ext = ext; 52 | this.md5 = md5; 53 | } 54 | 55 | public String getType() { 56 | return type; 57 | } 58 | 59 | public void setType(String type) { 60 | this.type = type; 61 | } 62 | 63 | public String getLastModifiedDate() { 64 | return lastModifiedDate; 65 | } 66 | 67 | public void setLastModifiedDate(String lastModifiedDate) { 68 | this.lastModifiedDate = lastModifiedDate; 69 | } 70 | 71 | public int getChunks() { 72 | return chunks; 73 | } 74 | 75 | public void setChunks(int chunks) { 76 | this.chunks = chunks; 77 | } 78 | 79 | public int getChunk() { 80 | return chunk; 81 | } 82 | 83 | public void setChunk(int chunk) { 84 | this.chunk = chunk; 85 | } 86 | 87 | public String getUserId() { 88 | return userId; 89 | } 90 | 91 | public void setUserId(String userId) { 92 | this.userId = userId; 93 | } 94 | 95 | public String getId() { 96 | return id; 97 | } 98 | 99 | public void setId(String id) { 100 | this.id = id; 101 | } 102 | 103 | public String getMd5() { 104 | return md5; 105 | } 106 | 107 | public void setMd5(String md5) { 108 | this.md5 = md5; 109 | } 110 | 111 | public int getChunkIndex() { 112 | return chunkIndex; 113 | } 114 | 115 | public void setChunkIndex(int chunkIndex) { 116 | this.chunkIndex = chunkIndex; 117 | } 118 | 119 | public String getSize() { 120 | return size; 121 | } 122 | 123 | public void setSize(String size) { 124 | this.size = size; 125 | } 126 | 127 | public String getName() { 128 | return name; 129 | } 130 | 131 | public void setName(String name) { 132 | this.name = name; 133 | } 134 | 135 | public String getExt() { 136 | return ext; 137 | } 138 | 139 | public void setExt(String ext) { 140 | this.ext = ext; 141 | } 142 | 143 | public String toString(){ 144 | return "name=" + this.name + "; size=" + this.size + "; chunkIndex=" + this.chunkIndex + "; md5=" + this.md5 145 | + "; userId=" + this.userId + "; id=" + this.id + "; chunks=" + this.chunks + "; chunk=" + this.chunk 146 | + "; lastModifiedDate=" + this.lastModifiedDate + "; type=" + this.type + "; ext=" + this.ext; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /serverJAVAEE/src/main/java/me/kazaff/wu/service/webUploader.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.service; 2 | 3 | import me.kazaff.wu.entity.FileInfo; 4 | import me.kazaff.wu.util.fileLock; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.context.annotation.Scope; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.io.*; 12 | import java.nio.channels.FileChannel; 13 | import java.security.MessageDigest; 14 | import java.security.NoSuchAlgorithmException; 15 | import java.util.*; 16 | import java.util.concurrent.locks.Lock; 17 | 18 | /** 19 | * Created by kazaff on 2014/12/2. 20 | */ 21 | 22 | @Service 23 | @Scope("prototype") 24 | public class webUploader { 25 | 26 | private final static Logger log = LoggerFactory.getLogger(webUploader.class); 27 | 28 | /** 29 | * 错误详情 30 | */ 31 | private String msg; 32 | 33 | /** 34 | * 秒传验证 35 | * 根据文件的MD5签名判断该文件是否已经存在 36 | * 37 | * @param key 文件的md5签名 38 | * @return 若存在则返回该文件的路径,不存在则返回null 39 | */ 40 | public String md5Check(String key){ 41 | 42 | //todo 模拟去数据库查找 43 | Map data = new HashMap(); 44 | data.put("b0201e4d41b2eeefc7d3d355a44c6f5a", "kazaff2.jpg"); 45 | 46 | if(data.containsKey(key)){ 47 | return data.get(key); 48 | }else{ 49 | return null; 50 | } 51 | } 52 | 53 | /** 54 | * 分片验证 55 | * 验证对应分片文件是否存在,大小是否吻合 56 | * @param file 分片文件的路径 57 | * @param size 分片文件的大小 58 | * @return 59 | */ 60 | public boolean chunkCheck(String file, Long size){ 61 | //检查目标分片是否存在且完整 62 | File target = new File(file); 63 | if(target.isFile() && size == target.length()){ 64 | return true; 65 | }else{ 66 | return false; 67 | } 68 | } 69 | 70 | /** 71 | * 分片合并操作 72 | * 要点: 73 | * > 合并: NIO 74 | * > 并发锁: 避免多线程同时触发合并操作 75 | * > 清理: 合并清理不再需要的分片文件、文件夹、tmp文件 76 | * @param folder 分片文件所在的文件夹名称 77 | * @param ext 合并后的文件后缀名 78 | * @param chunks 分片总数 79 | * @param md5 文件签名 80 | * @param path 合并后的文件所存储的位置 81 | * @return 82 | */ 83 | public String chunksMerge(String folder, String ext, int chunks, String md5, String path){ 84 | 85 | //合并后的目标文件 86 | String target; 87 | 88 | //检查是否满足合并条件:分片数量是否足够 89 | if(chunks == this.getChunksNum(path + "/" + folder)){ 90 | 91 | //同步指定合并的对象 92 | Lock lock = fileLock.getLock(folder); 93 | lock.lock(); 94 | try{ 95 | //检查是否满足合并条件:分片数量是否足够 96 | //File[] files = this.getChunks(path + "/" +folder); 97 | List files = new ArrayList(Arrays.asList(this.getChunks(path + "/" +folder))); 98 | if(chunks == files.size()){ 99 | 100 | //按照名称排序文件,这里分片都是按照数字命名的 101 | Collections.sort(files, new Comparator() { 102 | @Override 103 | public int compare(File o1, File o2) { 104 | if(Integer.valueOf(o1.getName()) < Integer.valueOf(o2.getName())){ 105 | return -1; 106 | } 107 | return 1; 108 | } 109 | }); 110 | 111 | //创建合并后的文件 112 | File outputFile = new File(path + "/" + this.randomFileName(ext)); 113 | if(outputFile.exists()){ 114 | log.error("文件[" + folder + "]随机命名冲突"); 115 | this.setErrorMsg("文件随机命名冲突"); 116 | return null; 117 | } 118 | outputFile.createNewFile(); 119 | FileChannel outChannel = new FileOutputStream(outputFile).getChannel(); 120 | 121 | //合并 122 | FileChannel inChannel; 123 | for(File file : files){ 124 | inChannel = new FileInputStream(file).getChannel(); 125 | inChannel.transferTo(0, inChannel.size(), outChannel); 126 | inChannel.close(); 127 | 128 | //删除分片 129 | if(!file.delete()){ 130 | log.error("分片[" + folder + "=>" + file.getName() + "]删除失败"); 131 | } 132 | } 133 | outChannel.close(); 134 | files = null; 135 | 136 | //将MD5签名和合并后的文件path存入持久层 137 | if(this.saveMd52FileMap(md5, outputFile.getName())){ 138 | log.error("文件[" + md5 + "=>" + outputFile.getName() + "]保存关系到持久成失败,但并不影响文件上传,只会导致日后该文件可能被重复上传而已"); 139 | } 140 | 141 | //清理:文件夹,tmp文件 142 | this.cleanSpace(folder, path); 143 | 144 | return outputFile.getName(); 145 | } 146 | }catch(Exception ex){ 147 | log.error("数据分片合并失败", ex); 148 | this.setErrorMsg("数据分片合并失败"); 149 | return null; 150 | 151 | }finally { 152 | //解锁 153 | lock.unlock(); 154 | //清理锁对象 155 | fileLock.removeLock(folder); 156 | } 157 | } 158 | 159 | //去持久层查找对应md5签名,直接返回对应path 160 | target = this.md5Check(md5); 161 | if(target == null){ 162 | log.error("文件[签名:" + md5 + "]数据不完整,可能该文件正在合并中"); 163 | this.setErrorMsg("数据不完整,可能该文件正在合并中"); 164 | return null; 165 | } 166 | 167 | return target; 168 | } 169 | 170 | /** 171 | * 将MD5签名和目标文件path的映射关系存入持久层 172 | * @param key md5签名 173 | * @param file 文件路径 174 | * @return 175 | */ 176 | public boolean saveMd52FileMap(String key, String file){ 177 | //todo 178 | return true; 179 | } 180 | 181 | /** 182 | * 为上传的文件创建对应的保存位置 183 | * 若上传的是分片,则会创建对应的文件夹结构和tmp文件 184 | * @param info 上传文件的相关信息 185 | * @param path 文件保存根路径 186 | * @return 187 | */ 188 | public File getReadySpace(FileInfo info, String path){ 189 | 190 | //创建上传文件所需的文件夹 191 | if(!this.createFileFolder(path, false)){ 192 | return null; 193 | } 194 | 195 | String newFileName; //上传文件的新名称 196 | 197 | //如果是分片上传,则需要为分片创建文件夹 198 | if (info.getChunks() > 0) { 199 | newFileName = String.valueOf(info.getChunk()); 200 | 201 | String fileFolder = this.md5(info.getUserId() + info.getName() + info.getType() + info.getLastModifiedDate() + info.getSize()); 202 | if(fileFolder == null){ 203 | return null; 204 | } 205 | 206 | path += "/" + fileFolder; //文件上传路径更新为指定文件信息签名后的临时文件夹,用于后期合并 207 | 208 | if(!this.createFileFolder(path, true)){ 209 | return null; 210 | } 211 | 212 | } else { 213 | //生成随机文件名 214 | newFileName = this.randomFileName(info.getName()); 215 | } 216 | 217 | return new File(path, newFileName); 218 | } 219 | 220 | /** 221 | * 清理分片上传的相关数据 222 | * 文件夹,tmp文件 223 | * @param folder 文件夹名称 224 | * @param path 上传文件根路径 225 | * @return 226 | */ 227 | private boolean cleanSpace(String folder, String path){ 228 | //删除分片文件夹 229 | File garbage = new File(path + "/" + folder); 230 | if(!garbage.delete()){ 231 | return false; 232 | } 233 | 234 | //删除tmp文件 235 | garbage = new File(path + "/" + folder + ".tmp"); 236 | if(!garbage.delete()){ 237 | return false; 238 | } 239 | 240 | return true; 241 | } 242 | 243 | /** 244 | * 获取指定文件的所有分片 245 | * @param folder 文件夹路径 246 | * @return 247 | */ 248 | private File[] getChunks(String folder){ 249 | File targetFolder = new File(folder); 250 | return targetFolder.listFiles(new FileFilter() { 251 | @Override 252 | public boolean accept(File file) { 253 | if (file.isDirectory()) { 254 | return false; 255 | } 256 | return true; 257 | } 258 | }); 259 | } 260 | 261 | /** 262 | * 获取指定文件的分片数量 263 | * @param folder 文件夹路径 264 | * @return 265 | */ 266 | private int getChunksNum(String folder){ 267 | 268 | File[] filesList = this.getChunks(folder); 269 | return filesList.length; 270 | } 271 | 272 | /** 273 | * 创建存放上传的文件的文件夹 274 | * @param file 文件夹路径 275 | * @return 276 | */ 277 | private boolean createFileFolder(String file, boolean hasTmp){ 278 | 279 | //创建存放分片文件的临时文件夹 280 | File tmpFile = new File(file); 281 | if(!tmpFile.exists()){ 282 | try { 283 | tmpFile.mkdir(); 284 | }catch(SecurityException ex){ 285 | log.error("无法创建文件夹", ex); 286 | this.setErrorMsg("无法创建文件夹"); 287 | return false; 288 | } 289 | } 290 | 291 | if(hasTmp){ 292 | //创建一个对应的文件,用来记录上传分片文件的修改时间,用于清理长期未完成的垃圾分片 293 | tmpFile = new File(file + ".tmp"); 294 | if(tmpFile.exists()){ 295 | tmpFile.setLastModified(System.currentTimeMillis()); 296 | }else{ 297 | try{ 298 | tmpFile.createNewFile(); 299 | }catch(IOException ex){ 300 | log.error("无法创建tmp文件", ex); 301 | this.setErrorMsg("无法创建tmp文件"); 302 | return false; 303 | } 304 | } 305 | } 306 | 307 | return true; 308 | } 309 | 310 | /** 311 | * 为上传的文件生成随机名称 312 | * @param originalName 文件的原始名称,主要用来获取文件的后缀名 313 | * @return 314 | */ 315 | private String randomFileName(String originalName){ 316 | String ext[] = originalName.split("\\."); 317 | return UUID.randomUUID().toString() + "." + ext[ext.length-1]; 318 | } 319 | 320 | /** 321 | * MD5签名 322 | * @param content 要签名的内容 323 | * @return 324 | */ 325 | private String md5(String content){ 326 | StringBuffer sb = new StringBuffer(); 327 | try{ 328 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 329 | md5.update(content.getBytes("UTF-8")); 330 | byte[] tmpFolder = md5.digest(); 331 | 332 | for(int i = 0; i < tmpFolder.length; i++){ 333 | sb.append(Integer.toString((tmpFolder[i] & 0xff) + 0x100, 16).substring(1)); 334 | } 335 | 336 | return sb.toString(); 337 | }catch(NoSuchAlgorithmException ex){ 338 | log.error("无法生成文件的MD5签名", ex); 339 | this.setErrorMsg("无法生成文件的MD5签名"); 340 | return null; 341 | }catch(UnsupportedEncodingException ex){ 342 | log.error("无法生成文件的MD5签名", ex); 343 | this.setErrorMsg("无法生成文件的MD5签名"); 344 | return null; 345 | } 346 | } 347 | 348 | /** 349 | * 记录异常错误信息 350 | * @param msg 错误详细 351 | */ 352 | private void setErrorMsg(String msg){ 353 | this.msg = msg; 354 | } 355 | 356 | /** 357 | * 获取错误详细 358 | * @return 359 | */ 360 | public String getErrorMsg(){ 361 | return this.msg; 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /serverJAVAEE/src/main/java/me/kazaff/wu/util/fileLock.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu.util; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.concurrent.locks.Lock; 8 | import java.util.concurrent.locks.ReentrantLock; 9 | 10 | /** 11 | * Created by kazaff on 2014/12/2. 12 | */ 13 | @Component 14 | public class fileLock { 15 | 16 | private static Map LOCKS = new HashMap(); 17 | 18 | public static synchronized Lock getLock(String key){ 19 | if(LOCKS.containsKey(key)){ 20 | return LOCKS.get(key); 21 | }else{ 22 | Lock one = new ReentrantLock(); 23 | LOCKS.put(key, one); 24 | return one; 25 | } 26 | } 27 | 28 | public static synchronized void removeLock(String key){ 29 | LOCKS.remove(key); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /serverJAVAEE/src/main/webapp/WEB-INF/configure.properties: -------------------------------------------------------------------------------- 1 | upload.folder=D:/code/test/webuploaderDemo/uploads -------------------------------------------------------------------------------- /serverJAVAEE/src/main/webapp/WEB-INF/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | TRACE 16 | 17 | 18 | 19 | 20 | ${logDir}/file-%d{yyyy-MM-dd}.log 21 | 10 22 | 23 | 24 | 25 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n 26 | UTF-8 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /serverJAVAEE/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | text/html;charset=UTF-8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | error_fileupload 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | classpath:../configure.properties 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /serverJAVAEE/src/main/webapp/WEB-INF/pages/error_fileupload.jsp: -------------------------------------------------------------------------------- 1 | <%-- 2 | Created by IntelliJ IDEA. 3 | User: kazaff 4 | Date: 2014/12/1 5 | Time: 9:01 6 | To change this template use File | Settings | File Templates. 7 | --%> 8 | <%@ page contentType="text/html;charset=UTF-8" language="java" session="false" %> 9 | {"ifExist":0, "message": "file size too big!"} -------------------------------------------------------------------------------- /serverJAVAEE/src/main/webapp/WEB-INF/pages/hello.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 |

${message}

4 | 5 | -------------------------------------------------------------------------------- /serverJAVAEE/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | Spring MVC Application 7 | 8 | 9 | mvc-dispatcher 10 | org.springframework.web.servlet.DispatcherServlet 11 | 1 12 | 13 | 14 | 15 | mvc-dispatcher 16 | / 17 | 18 | 19 | 20 | cors 21 | me.kazaff.filters.CORSFilter 22 | 23 | 24 | 25 | cors 26 | /* 27 | 28 | 29 | 30 | 31 | logbackConfigLocation 32 | WEB-INF/logback.xml 33 | 34 | 35 | ch.qos.logback.ext.spring.web.LogbackConfigListener 36 | 37 | -------------------------------------------------------------------------------- /serverJAVAEE/src/test/java/me/kazaff/wu/AppTests.java: -------------------------------------------------------------------------------- 1 | package me.kazaff.wu; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.test.context.ContextConfiguration; 8 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 9 | import org.springframework.test.context.web.WebAppConfiguration; 10 | import org.springframework.test.web.servlet.MockMvc; 11 | import org.springframework.web.context.WebApplicationContext; 12 | 13 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 14 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 15 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; 16 | import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 17 | 18 | @RunWith(SpringJUnit4ClassRunner.class) 19 | @WebAppConfiguration 20 | @ContextConfiguration("file:src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml") 21 | public class AppTests { 22 | private MockMvc mockMvc; 23 | 24 | @SuppressWarnings("SpringJavaAutowiringInspection") 25 | @Autowired 26 | protected WebApplicationContext wac; 27 | 28 | @Before 29 | public void setup() { 30 | this.mockMvc = webAppContextSetup(this.wac).build(); 31 | } 32 | 33 | @Test 34 | public void simple() throws Exception { 35 | mockMvc.perform(get("/")) 36 | .andExpect(status().isOk()) 37 | .andExpect(view().name("hello")); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /serverNODE/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by kazaff on 2014/11/12. 3 | * 本代码用于测试webuploader的相关特性,存在大量可优化空间,不建议使用在正式项目中 4 | */ 5 | 6 | var formidable = require("formidable"), 7 | http = require("http"), 8 | util = require("util"), 9 | fs = require("fs"), 10 | path = require("path"), 11 | _ = require("underscore"), 12 | config = require("./config"), 13 | wu = require("./webuploader"); 14 | 15 | http.createServer(function(req, res){ 16 | 17 | //用于分片合并时的同步标识位 18 | var lockMark = []; 19 | 20 | //跨域 21 | res.writeHead(200, { 22 | "Access-Control-Allow-Origin": "*", 23 | "Access-Control-Allow-Methods": "POST" 24 | }); 25 | 26 | var action = req.method.toLowerCase(); 27 | 28 | if(action == 'post' && req.url == "/fileUpload"){ 29 | 30 | var form = new formidable.IncomingForm({uploadDir:"tmp"}); //避免EXDEV问题 31 | form.parse(req, function(err, fields, files){ 32 | 33 | //console.log(util.inspect({fields: fields, files: files})); 34 | 35 | if(_.isUndefined(fields.status)){ //分片上传 36 | //分片的元数据必须以文件的形式(当然数据库也行)持久化,而不应该持久化在node的全局变量中,避免node进程重启而导致的元数据丢失,这里处理的方式参照php版本的后端 37 | //具体详情可见https://github.com/kazaff/me.kazaff.article/blob/master/%E8%81%8A%E8%81%8A%E5%A4%A7%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0.md 38 | 39 | var upDir = ""; 40 | var isChunks = !(_.isUndefined(fields.chunks) || parseInt(fields.chunks) <= 0); 41 | if(isChunks){ 42 | upDir = path.join(config.uploadDir, wu.createUniqueFileName(fields)); 43 | }else{ 44 | upDir = config.uploadDir; 45 | } 46 | 47 | fs.mkdir(upDir, function(err){ 48 | if(_.isNull(err) || err.code === "EEXIST"){ 49 | 50 | var newFileName = ""; 51 | 52 | if(isChunks){ 53 | //更新tmp文件的修改时间 54 | fs.open(upDir+".tmp", "w", function(err, fd){ 55 | if(err){ 56 | //todo 57 | console.error(err); 58 | 59 | }else{ 60 | var time = new Date(); 61 | fs.futimes(fd, time, time, function(err){ 62 | if(err){ 63 | //todo 64 | console.error(err); 65 | } 66 | 67 | fs.close(fd); 68 | }); 69 | } 70 | }); 71 | 72 | 73 | newFileName = fields.chunk; 74 | }else{ 75 | newFileName = wu.randomFileName(path.extname(files.file.name)); 76 | } 77 | 78 | fs.rename(files.file.path, path.join(upDir, newFileName), function(err){ 79 | if(err){ 80 | //todo 81 | console.error(err); 82 | res.end('{"status":0}'); 83 | return ; 84 | } 85 | 86 | res.end('{"status":1, "path":'+ newFileName +'}'); 87 | }); 88 | 89 | }else{ 90 | //todo 91 | console.error(err); 92 | res.end('{"status":0}'); 93 | } 94 | }); 95 | 96 | 97 | }else if(fields.status == "md5Check"){ //秒传校验 98 | 99 | //todo 模拟去数据库中校验md5是否存在 100 | if(fields.md5 == "b0201e4d41b2eeefc7d3d355a44c6f5a"){ 101 | res.end('{"ifExist":1, "path":"kazaff2.jpg"}'); 102 | }else{ 103 | res.end('{"ifExist":0}'); 104 | } 105 | 106 | 107 | }else if(fields.status == "chunkCheck"){ //分片校验 108 | 109 | fs.stat(path.join(config.uploadDir, fields.name, fields.chunkIndex), function(err, stats){ 110 | if(err || stats.size != fields.size){ 111 | res.end('{"ifExist":0}'); 112 | }else{ 113 | res.end('{"ifExist":1}'); 114 | } 115 | }); 116 | }else if(fields.status == "chunksMerge"){ //分片合并 117 | 118 | //同步机制 119 | if(_.contains(lockMark, fields.name)){ 120 | 121 | res.end('{"status":0}'); 122 | }else{ 123 | 124 | lockMark.push(fields.name); 125 | 126 | var newFileName = wu.randomFileName(fields.ext); 127 | var targetStream = fs.createWriteStream(path.join(config.uploadDir, newFileName)); 128 | wu.chunksMerge(path.join(config.uploadDir, fields.name), targetStream, fields.chunks, function(err){ 129 | 130 | if(err){ 131 | //todo 132 | console.error(err); 133 | res.end('{"status":0}'); 134 | return ; 135 | } 136 | 137 | targetStream.end(function(){ 138 | //删除文件夹和tmp 139 | fs.unlink(path.join(config.uploadDir, fields.name) + ".tmp", function(err){ 140 | if(err){ 141 | //todo 142 | console.error(err); 143 | } 144 | }); 145 | fs.rmdir(path.join(config.uploadDir, fields.name), function(err){ 146 | if(err){ 147 | //todo 148 | console.error(err); 149 | } 150 | }); 151 | 152 | lockMark = _.without(lockMark, fields.name); 153 | 154 | //todo 这里其实需要把该文件和其前端校验的md5保存在数据库中,供秒传功能检索 155 | 156 | res.end('{"status":1, "path":"' + newFileName + '"}'); 157 | }); 158 | 159 | }); 160 | } 161 | } 162 | 163 | }); 164 | 165 | return; 166 | 167 | }else if(action != 'options'){ 168 | res.writeHead(404); 169 | } 170 | 171 | res.end(); 172 | 173 | }).listen(config.port, config.host); -------------------------------------------------------------------------------- /serverNODE/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by kazaff on 2014/11/13. 3 | */ 4 | 5 | module.exports = { 6 | host: "127.0.0.1", 7 | port: 3000, 8 | uploadDir: "../uploads" 9 | }; -------------------------------------------------------------------------------- /serverNODE/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webuploaderDemo", 3 | "description": "a demo for webuploader", 4 | "version": "0.0.1", 5 | "private": true, 6 | "author": { 7 | "name": "kazaff", 8 | "email": "edisondik@163.com", 9 | "url": "http://blog.kazaff.me" 10 | }, 11 | "dependencies": { 12 | "formidable": "^1.0.15", 13 | "underscore": "^1.7.0", 14 | "node-uuid": "^1.4.1", 15 | "async": "^0.9.0" 16 | }, 17 | "engines": { 18 | "node": ">=0.10.25" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /serverNODE/webuploader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by kazaff on 2014/11/13. 3 | */ 4 | 5 | var crypto = require("crypto"), 6 | uuid = require("node-uuid"), 7 | async = require("async"), 8 | path = require("path"), 9 | fs = require("fs"); 10 | 11 | module.exports = { 12 | createUniqueFileName: function(info){ 13 | //md5(当前登录用户的数据库id + 文件原始名称 + 文件类型 + 文件最后修改时间 + 文件总大小) 14 | var str = "" + info.userId + info.name + info.type + info.lastModifiedDate + info.size; 15 | return crypto.createHash("md5").update(str, "utf8").digest("hex"); 16 | }, 17 | randomFileName: function(extname){ 18 | return uuid.v4() + "." +extname; 19 | }, 20 | chunksMerge: function(dir, targetSteam, total, callback){ 21 | 22 | var index = 0; 23 | async.whilst(function(){ 24 | return index < total; 25 | }, function(cb){ 26 | 27 | var originStream = fs.createReadStream(path.join(dir, index.toString())); 28 | originStream.pipe(targetSteam, {end: false}); 29 | originStream.on("end", function(){ 30 | //删除文件 31 | fs.unlink(path.join(dir, index.toString()), function(err){ 32 | 33 | if(err){ 34 | //todo 35 | console.error(err); 36 | } 37 | }); 38 | 39 | index++; 40 | cb(); 41 | }); 42 | 43 | }, function(err){ 44 | 45 | callback(err); 46 | }); 47 | 48 | } 49 | }; -------------------------------------------------------------------------------- /serverPHP/fileUpload.php: -------------------------------------------------------------------------------- 1 | setOption($key, $val); 42 | } 43 | return $this; 44 | } 45 | 46 | /** 47 | * 调用该方法上传文件 48 | * Enter description here ... 49 | * @param $fileField 上传文件的表单名称 50 | */ 51 | function upload($fileField, $info){ 52 | 53 | //判断是否为分块上传 54 | $this->checkChunk($info); 55 | 56 | if (!$this->checkFilePath($this->path)){ 57 | $this->errorMess = $this->getError(); 58 | return false; 59 | } 60 | 61 | //将文件上传的信息取出赋给变量 62 | $name = $_FILES[$fileField]['name']; 63 | $tmp_name = $_FILES[$fileField]['tmp_name']; 64 | $size = $_FILES[$fileField]['size']; 65 | $error = $_FILES[$fileField]['error']; 66 | 67 | //设置文件信息 68 | if ($this->setFiles($name, $tmp_name, $size, $error)){ 69 | 70 | //如果是分块,则创建一个唯一名称的文件夹用来保存该文件的所有分块 71 | if($this->isChunk){ 72 | $uploadDir = $this->path; 73 | $tmpName = $this->setDirNameForChunks($info); 74 | if(!$this->checkFilePath($uploadDir . '/' . $tmpName)){ 75 | $this->errorMess = $this->getError(); 76 | return false; 77 | } 78 | 79 | //创建一个对应的文件,用来记录上传分块文件的修改时间,用于清理长期未完成的垃圾分块 80 | touch($uploadDir.'/'.$tmpName.'.tmp'); 81 | } 82 | 83 | if($this->checkFileSize() && $this->checkFileType()){ 84 | $this->setNewFileName(); 85 | if ($this->copyFile()){ 86 | return $this->newFileName; 87 | } 88 | } 89 | } 90 | 91 | $this->errorMess = $this->getError(); 92 | return false; 93 | } 94 | 95 | public function chunksMerge($uniqueFileName, $chunksTotal, $fileExt){ 96 | 97 | $targetDir = $this->path.'/'.$uniqueFileName; 98 | 99 | //检查对应文件夹中的分块文件数量是否和总数保持一致 100 | if($chunksTotal > 1 && (count(scandir($targetDir)) - 2) == $chunksTotal){ 101 | //同步锁机制 102 | $lockFd = fopen($this->path.'/'.$uniqueFileName.'.lock', "w"); 103 | if(!flock($lockFd, LOCK_EX | LOCK_NB)){ 104 | fclose($lockFd); 105 | return false; 106 | } 107 | 108 | //进行合并 109 | $this->fileType = $fileExt; 110 | $finalName = $this->path.'/'.($this->setOption('newFileName', $this->proRandName())); 111 | $file = fopen($finalName, 'wb'); 112 | for($index = 0; $index < $chunksTotal; $index++){ 113 | $tmpFile = $targetDir.'/'.$index; 114 | $chunkFile = fopen($tmpFile, 'rb'); 115 | $content = fread($chunkFile, filesize($tmpFile)); 116 | fclose($chunkFile); 117 | fwrite($file, $content); 118 | 119 | //删除chunk文件 120 | unlink($tmpFile); 121 | } 122 | 123 | fclose($file); 124 | 125 | //删除chunk文件夹 126 | rmdir($targetDir); 127 | unlink($this->path.'/'.$uniqueFileName.'.tmp'); 128 | 129 | //解锁 130 | flock($lockFd, LOCK_UN); 131 | fclose($lockFd); 132 | unlink($this->path.'/'.$uniqueFileName.'.lock'); 133 | 134 | return $this->newFileName; 135 | 136 | } 137 | return false; 138 | } 139 | 140 | //获取上传后的文件名称 141 | public function getFileName(){ 142 | return $this->newFileName; 143 | } 144 | 145 | //上传失败后,调用该方法则返回,上传出错信息 146 | public function getErrorMsg(){ 147 | return $this->errorMess; 148 | } 149 | 150 | //设置上传出错信息 151 | public function getError(){ 152 | $str = "上传文件{$this->originName}时出错:"; 153 | switch ($this->errorNum) { 154 | case 4: 155 | $str.= "没有文件被上传"; 156 | break; 157 | case 3: 158 | $str.= "文件只有部分被上传"; 159 | break; 160 | case 2: 161 | $str.= "上传文件的大小超过了HTML表单中MAX_FILE_SIZE选项指定的值"; 162 | break; 163 | case 1: 164 | $str.= "上传的文件超过了php.ini中upload_max_filesize选项限制的值"; 165 | break; 166 | case -1: 167 | $str.= "未允许的类型"; 168 | break; 169 | case -2: 170 | $str.= "文件过大, 上传的文件夹不能超过{$this->maxsize}个字节"; 171 | break; 172 | case -3: 173 | $str.= "上传失败"; 174 | break; 175 | case -4: 176 | $str.= "建立存放上传文件目录失败,请重新指定上传目录"; 177 | break; 178 | case -5: 179 | $str.= "必须指定上传文件的路径"; 180 | break; 181 | 182 | default: 183 | $str .= "未知错误"; 184 | } 185 | return $str."
"; 186 | } 187 | 188 | //根据文件的相关信息为分块数据创建文件夹 189 | //md5(当前登录用户的数据库id + 文件原始名称 + 文件类型 + 文件最后修改时间 + 文件总大小) 190 | private function setDirNameForChunks($info){ 191 | $str = ''.$info['userId'] . $info['name'] . $info['type'] . $info['lastModifiedDate'] . $info['size']; 192 | return md5($str); 193 | } 194 | 195 | //设置和$_FILES有关的内容 196 | private function setFiles($name="", $tmp_name="", $size=0, $error=0){ 197 | $this->setOption('errorNum', $error); 198 | if ($error) { 199 | return false; 200 | } 201 | $this->setOption('originName', $name); 202 | $this->setOption('tmpFileName', $tmp_name); 203 | $aryStr = explode(".", $name); 204 | $this->setOption("fileType", strtolower($aryStr[count($aryStr)-1])); 205 | $this->setOption("fileSize", $size); 206 | return true; 207 | } 208 | 209 | private function checkChunk($info){ 210 | if(isset($info['chunks']) && $info['chunks'] > 0){ 211 | $this->setOption("isChunk", true); 212 | 213 | if(isset($info['chunk']) && $info['chunk'] >= 0){ 214 | $this->setOption("indexOfChunk", $info['chunk']); 215 | 216 | return true; 217 | } 218 | 219 | throw new Exception('分块索引不合法'); 220 | } 221 | 222 | return false; 223 | } 224 | 225 | //为单个成员属性设置值 226 | private function setOption($key, $val){ 227 | $this->$key = $val; 228 | return $val; 229 | } 230 | 231 | //设置上传后的文件名称 232 | private function setNewFileName(){ 233 | if($this->isChunk){ //如果是分块,则以分块的索引作为文件名称保存 234 | $this->setOption('newFileName', $this->indexOfChunk); 235 | }elseif($this->israndname) { 236 | $this->setOption('newFileName', $this->proRandName()); 237 | }else{ 238 | $this->setOption('newFileName', $this->originName); 239 | } 240 | } 241 | 242 | //检查上传的文件是否是合法的类型 243 | private function checkFileType(){ 244 | if (in_array(strtolower($this->fileType), $this->allowtype)) { 245 | return true; 246 | }else{ 247 | $this->setOption('errorNum', -1); 248 | return false; 249 | } 250 | } 251 | 252 | 253 | //检查上传的文件是否是允许的大小 254 | private function checkFileSize(){ 255 | if ($this->fileSize > $this->maxsize) { 256 | $this->setOption('errorNum', -5); 257 | return false; 258 | }else{ 259 | return true; 260 | } 261 | } 262 | 263 | //检查是否有存放上传文件的目录 264 | private function checkFilePath($target){ 265 | 266 | if (empty($target)) { 267 | $this->setOption('errorNum', -5); 268 | return false; 269 | } 270 | 271 | if (!file_exists($target) || !is_writable($target)) { 272 | if (!@mkdir($target, 0755)) { 273 | $this->setOption('errorNum', -4); 274 | return false; 275 | } 276 | } 277 | 278 | $this->path = $target; 279 | return true; 280 | } 281 | 282 | //设置随机文件名 283 | private function proRandName(){ 284 | $fileName = date('YmdHis')."_".rand(100,999); 285 | return $fileName.'.'.$this->fileType; 286 | } 287 | 288 | //复制上传文件到指定的位置 289 | private function copyFile(){ 290 | if (!$this->errorNum) { 291 | $path = rtrim($this->path, '/').'/'; 292 | $path.= $this->newFileName; 293 | if (@move_uploaded_file($this->tmpFileName, $path)) { 294 | return true; 295 | }else{ 296 | $this->setOption('errorNum', -3); 297 | return false; 298 | } 299 | }else{ 300 | return false; 301 | } 302 | } 303 | } 304 | 305 | //关闭缓存 306 | header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); 307 | header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 308 | header("Cache-Control: no-store, no-cache, must-revalidate"); 309 | header("Cache-Control: post-check=0, pre-check=0", false); 310 | header("Pragma: no-cache"); 311 | 312 | $uploader = new FileUpload(); 313 | 314 | //用于断点续传,验证指定分块是否已经存在,避免重复上传 315 | if(isset($_POST['status'])){ 316 | if($_POST['status'] == 'chunkCheck'){ 317 | $target = '../uploads/'.$_POST['name'].'/'.$_POST['chunkIndex']; 318 | if(file_exists($target) && filesize($target) == $_POST['size']){ 319 | die('{"ifExist":1}'); 320 | } 321 | die('{"ifExist":0}'); 322 | 323 | }elseif($_POST['status'] == 'md5Check'){ 324 | 325 | //todo 模拟持久层查询 326 | $dataArr = array( 327 | 'b0201e4d41b2eeefc7d3d355a44c6f5a' => 'kazaff2.jpg' 328 | ); 329 | 330 | if(isset($dataArr[$_POST['md5']])){ 331 | die('{"ifExist":1, "path":"'.$dataArr[$_POST['md5']].'"}'); 332 | } 333 | die('{"ifExist":0}'); 334 | }elseif($_POST['status'] == 'chunksMerge'){ 335 | 336 | if($path = $uploader->chunksMerge($_POST['name'], $_POST['chunks'], $_POST['ext'])){ 337 | //todo 把md5签名存入持久层,供未来的秒传验证 338 | die('{"status":1, "path": "'.$path.'"}'); 339 | } 340 | die('{"status":0'); 341 | } 342 | } 343 | 344 | if(($path = $uploader->upload('file', $_POST)) !== false){ 345 | die('{"status":1, "path": "'.$path.'"}'); 346 | } 347 | die('{"status":0}'); 348 | 349 | -------------------------------------------------------------------------------- /webuploader.css: -------------------------------------------------------------------------------- 1 | .webuploader-container { 2 | position: relative; 3 | } 4 | .webuploader-element-invisible { 5 | position: absolute !important; 6 | clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ 7 | clip: rect(1px,1px,1px,1px); 8 | } 9 | .webuploader-pick { 10 | position: relative; 11 | display: inline-block; 12 | cursor: pointer; 13 | background: #00b7ee; 14 | padding: 10px 15px; 15 | color: #fff; 16 | text-align: center; 17 | border-radius: 3px; 18 | overflow: hidden; 19 | } 20 | .webuploader-pick-hover { 21 | background: #00a2d4; 22 | } 23 | 24 | .webuploader-pick-disable { 25 | opacity: 0.6; 26 | pointer-events:none; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /webuploader.flashonly.min.js: -------------------------------------------------------------------------------- 1 | /* WebUploader 0.1.5 */!function(a,b){var c,d={},e=function(a,b){var c,d,e;if("string"==typeof a)return h(a);for(c=[],d=a.length,e=0;d>e;e++)c.push(h(a[e]));return b.apply(null,c)},f=function(a,b,c){2===arguments.length&&(c=b,b=null),e(b||[],function(){g(a,c,arguments)})},g=function(a,b,c){var f,g={exports:b};"function"==typeof b&&(c.length||(c=[e,g.exports,g]),f=b.apply(null,c),void 0!==f&&(g.exports=f)),d[a]=g.exports},h=function(b){var c=d[b]||a[b];if(!c)throw new Error("`"+b+"` is undefined");return c},i=function(a){var b,c,e,f,g,h;h=function(a){return a&&a.charAt(0).toUpperCase()+a.substr(1)};for(b in d)if(c=a,d.hasOwnProperty(b)){for(e=b.split("/"),g=h(e.pop());f=h(e.shift());)c[f]=c[f]||{},c=c[f];c[g]=d[b]}return a},j=function(c){return a.__dollar=c,i(b(a,f,e))};"object"==typeof module&&"object"==typeof module.exports?module.exports=j():"function"==typeof define&&define.amd?define(["jquery"],j):(c=a.WebUploader,a.WebUploader=j(),a.WebUploader.noConflict=function(){a.WebUploader=c})}(window,function(a,b,c){return b("dollar-third",[],function(){var b=a.__dollar||a.jQuery||a.Zepto;if(!b)throw new Error("jQuery or Zepto not found!");return b}),b("dollar",["dollar-third"],function(a){return a}),b("promise-third",["dollar"],function(a){return{Deferred:a.Deferred,when:a.when,isPromise:function(a){return a&&"function"==typeof a.then}}}),b("promise",["promise-third"],function(a){return a}),b("base",["dollar","promise"],function(b,c){function d(a){return function(){return h.apply(a,arguments)}}function e(a,b){return function(){return a.apply(b,arguments)}}function f(a){var b;return Object.create?Object.create(a):(b=function(){},b.prototype=a,new b)}var g=function(){},h=Function.call;return{version:"0.1.5",$:b,Deferred:c.Deferred,isPromise:c.isPromise,when:c.when,browser:function(a){var b={},c=a.match(/WebKit\/([\d.]+)/),d=a.match(/Chrome\/([\d.]+)/)||a.match(/CriOS\/([\d.]+)/),e=a.match(/MSIE\s([\d\.]+)/)||a.match(/(?:trident)(?:.*rv:([\w.]+))?/i),f=a.match(/Firefox\/([\d.]+)/),g=a.match(/Safari\/([\d.]+)/),h=a.match(/OPR\/([\d.]+)/);return c&&(b.webkit=parseFloat(c[1])),d&&(b.chrome=parseFloat(d[1])),e&&(b.ie=parseFloat(e[1])),f&&(b.firefox=parseFloat(f[1])),g&&(b.safari=parseFloat(g[1])),h&&(b.opera=parseFloat(h[1])),b}(navigator.userAgent),os:function(a){var b={},c=a.match(/(?:Android);?[\s\/]+([\d.]+)?/),d=a.match(/(?:iPad|iPod|iPhone).*OS\s([\d_]+)/);return c&&(b.android=parseFloat(c[1])),d&&(b.ios=parseFloat(d[1].replace(/_/g,"."))),b}(navigator.userAgent),inherits:function(a,c,d){var e;return"function"==typeof c?(e=c,c=null):e=c&&c.hasOwnProperty("constructor")?c.constructor:function(){return a.apply(this,arguments)},b.extend(!0,e,a,d||{}),e.__super__=a.prototype,e.prototype=f(a.prototype),c&&b.extend(!0,e.prototype,c),e},noop:g,bindFn:e,log:function(){return a.console?e(console.log,console):g}(),nextTick:function(){return function(a){setTimeout(a,1)}}(),slice:d([].slice),guid:function(){var a=0;return function(b){for(var c=(+new Date).toString(32),d=0;5>d;d++)c+=Math.floor(65535*Math.random()).toString(32);return(b||"wu_")+c+(a++).toString(32)}}(),formatSize:function(a,b,c){var d;for(c=c||["B","K","M","G","TB"];(d=c.shift())&&a>1024;)a/=1024;return("B"===d?a:a.toFixed(b||2))+d}}}),b("mediator",["base"],function(a){function b(a,b,c,d){return f.grep(a,function(a){return!(!a||b&&a.e!==b||c&&a.cb!==c&&a.cb._cb!==c||d&&a.ctx!==d)})}function c(a,b,c){f.each((a||"").split(h),function(a,d){c(d,b)})}function d(a,b){for(var c,d=!1,e=-1,f=a.length;++e1?void(d.isPlainObject(b)&&d.isPlainObject(c[a])?d.extend(c[a],b):c[a]=b):a?c[a]:c},getStats:function(){var a=this.request("get-stats");return a?{successNum:a.numOfSuccess,progressNum:a.numOfProgress,cancelNum:a.numOfCancel,invalidNum:a.numOfInvalid,uploadFailNum:a.numOfUploadFailed,queueNum:a.numOfQueue,interruptNum:a.numofInterrupt}:{}},trigger:function(a){var c=[].slice.call(arguments,1),e=this.options,f="on"+a.substring(0,1).toUpperCase()+a.substring(1);return b.trigger.apply(this,arguments)===!1||d.isFunction(e[f])&&e[f].apply(this,c)===!1||d.isFunction(this[f])&&this[f].apply(this,c)===!1||b.trigger.apply(b,[this,a].concat(c))===!1?!1:!0},destroy:function(){this.request("destroy",arguments),this.off()},request:a.noop}),a.create=c.create=function(a){return new c(a)},a.Uploader=c,c}),b("runtime/runtime",["base","mediator"],function(a,b){function c(b){this.options=d.extend({container:document.body},b),this.uid=a.guid("rt_")}var d=a.$,e={},f=function(a){for(var b in a)if(a.hasOwnProperty(b))return b;return null};return d.extend(c.prototype,{getContainer:function(){var a,b,c=this.options;return this._container?this._container:(a=d(c.container||document.body),b=d(document.createElement("div")),b.attr("id","rt_"+this.uid),b.css({position:"absolute",top:"0px",left:"0px",width:"1px",height:"1px",overflow:"hidden"}),a.append(b),a.addClass("webuploader-container"),this._container=b,this._parent=a,b)},init:a.noop,exec:a.noop,destroy:function(){this._container&&this._container.remove(),this._parent&&this._parent.removeClass("webuploader-container"),this.off()}}),c.orders="html5,flash",c.addRuntime=function(a,b){e[a]=b},c.hasRuntime=function(a){return!!(a?e[a]:f(e))},c.create=function(a,b){var g,h;if(b=b||c.orders,d.each(b.split(/\s*,\s*/g),function(){return e[this]?(g=this,!1):void 0}),g=g||f(e),!g)throw new Error("Runtime Error");return h=new e[g](a)},b.installTo(c.prototype),c}),b("runtime/client",["base","mediator","runtime/runtime"],function(a,b,c){function d(b,d){var f,g=a.Deferred();this.uid=a.guid("client_"),this.runtimeReady=function(a){return g.done(a)},this.connectRuntime=function(b,h){if(f)throw new Error("already connected!");return g.done(h),"string"==typeof b&&e.get(b)&&(f=e.get(b)),f=f||e.get(null,d),f?(a.$.extend(f.options,b),f.__promise.then(g.resolve),f.__client++):(f=c.create(b,b.runtimeOrder),f.__promise=g.promise(),f.once("ready",g.resolve),f.init(),e.add(f),f.__client=1),d&&(f.__standalone=d),f},this.getRuntime=function(){return f},this.disconnectRuntime=function(){f&&(f.__client--,f.__client<=0&&(e.remove(f),delete f.__promise,f.destroy()),f=null)},this.exec=function(){if(f){var c=a.slice(arguments);return b&&c.unshift(b),f.exec.apply(this,c)}},this.getRuid=function(){return f&&f.uid},this.destroy=function(a){return function(){a&&a.apply(this,arguments),this.trigger("destroy"),this.off(),this.exec("destroy"),this.disconnectRuntime()}}(this.destroy)}var e;return e=function(){var a={};return{add:function(b){a[b.uid]=b},get:function(b,c){var d;if(b)return a[b];for(d in a)if(!c||!a[d].__standalone)return a[d];return null},remove:function(b){delete a[b.uid]}}}(),b.installTo(d.prototype),d}),b("lib/blob",["base","runtime/client"],function(a,b){function c(a,c){var d=this;d.source=c,d.ruid=a,this.size=c.size||0,this.type=!c.type&&this.ext&&~"jpg,jpeg,png,gif,bmp".indexOf(this.ext)?"image/"+("jpg"===this.ext?"jpeg":this.ext):c.type||"application/octet-stream",b.call(d,"Blob"),this.uid=c.uid||this.uid,a&&d.connectRuntime(a)}return a.inherits(b,{constructor:c,slice:function(a,b){return this.exec("slice",a,b)},getSource:function(){return this.source}}),c}),b("lib/file",["base","lib/blob"],function(a,b){function c(a,c){var f;this.name=c.name||"untitled"+d++,f=e.exec(c.name)?RegExp.$1.toLowerCase():"",!f&&c.type&&(f=/\/(jpg|jpeg|png|gif|bmp)$/i.exec(c.type)?RegExp.$1.toLowerCase():"",this.name+="."+f),this.ext=f,this.lastModifiedDate=c.lastModifiedDate||(new Date).toLocaleString(),b.apply(this,arguments)}var d=1,e=/\.([^.]+)$/;return a.inherits(b,c)}),b("lib/filepicker",["base","runtime/client","lib/file"],function(b,c,d){function e(a){if(a=this.options=f.extend({},e.options,a),a.container=f(a.id),!a.container.length)throw new Error("按钮指定错误");a.innerHTML=a.innerHTML||a.label||a.container.html()||"",a.button=f(a.button||document.createElement("div")),a.button.html(a.innerHTML),a.container.html(a.button),c.call(this,"FilePicker",!0)}var f=b.$;return e.options={button:null,container:null,label:null,innerHTML:null,multiple:!0,accept:null,name:"file"},b.inherits(c,{constructor:e,init:function(){var c=this,e=c.options,g=e.button;g.addClass("webuploader-pick"),c.on("all",function(a){var b;switch(a){case"mouseenter":g.addClass("webuploader-pick-hover");break;case"mouseleave":g.removeClass("webuploader-pick-hover");break;case"change":b=c.exec("getFiles"),c.trigger("select",f.map(b,function(a){return a=new d(c.getRuid(),a),a._refer=e.container,a}),e.container)}}),c.connectRuntime(e,function(){c.refresh(),c.exec("init",e),c.trigger("ready")}),this._resizeHandler=b.bindFn(this.refresh,this),f(a).on("resize",this._resizeHandler)},refresh:function(){var a=this.getRuntime().getContainer(),b=this.options.button,c=b.outerWidth?b.outerWidth():b.width(),d=b.outerHeight?b.outerHeight():b.height(),e=b.offset();c&&d&&a.css({bottom:"auto",right:"auto",width:c+"px",height:d+"px"}).offset(e)},enable:function(){var a=this.options.button;a.removeClass("webuploader-pick-disable"),this.refresh()},disable:function(){var a=this.options.button;this.getRuntime().getContainer().css({top:"-99999px"}),a.addClass("webuploader-pick-disable")},destroy:function(){var b=this.options.button;f(a).off("resize",this._resizeHandler),b.removeClass("webuploader-pick-disable webuploader-pick-hover webuploader-pick")}}),e}),b("widgets/widget",["base","uploader"],function(a,b){function c(a){if(!a)return!1;var b=a.length,c=e.type(a);return 1===a.nodeType&&b?!0:"array"===c||"function"!==c&&"string"!==c&&(0===b||"number"==typeof b&&b>0&&b-1 in a)}function d(a){this.owner=a,this.options=a.options}var e=a.$,f=b.prototype._init,g=b.prototype.destroy,h={},i=[];return e.extend(d.prototype,{init:a.noop,invoke:function(a,b){var c=this.responseMap;return c&&a in c&&c[a]in this&&e.isFunction(this[c[a]])?this[c[a]].apply(this,b):h},request:function(){return this.owner.request.apply(this.owner,arguments)}}),e.extend(b.prototype,{_init:function(){var a=this,b=a._widgets=[],c=a.options.disableWidgets||"";return e.each(i,function(d,e){(!c||!~c.indexOf(e._name))&&b.push(new e(a))}),f.apply(a,arguments)},request:function(b,d,e){var f,g,i,j,k=0,l=this._widgets,m=l&&l.length,n=[],o=[];for(d=c(d)?d:[d];m>k;k++)f=l[k],g=f.invoke(b,d),g!==h&&(a.isPromise(g)?o.push(g):n.push(g));return e||o.length?(i=a.when.apply(a,o),j=i.pipe?"pipe":"then",i[j](function(){var b=a.Deferred(),c=arguments;return 1===c.length&&(c=c[0]),setTimeout(function(){b.resolve(c)},1),b.promise()})[e?j:"done"](e||a.noop)):n[0]},destroy:function(){g.apply(this,arguments),this._widgets=null}}),b.register=d.register=function(b,c){var f,g={init:"init",destroy:"destroy",name:"anonymous"};return 1===arguments.length?(c=b,e.each(c,function(a){return"_"===a[0]||"name"===a?void("name"===a&&(g.name=c.name)):void(g[a.replace(/[A-Z]/g,"-$&").toLowerCase()]=a)})):g=e.extend(g,b),c.responseMap=g,f=a.inherits(d,c),f._name=g.name,i.push(f),f},b.unRegister=d.unRegister=function(a){if(a&&"anonymous"!==a)for(var b=i.length;b--;)i[b]._name===a&&i.splice(b,1)},d}),b("widgets/filepicker",["base","uploader","lib/filepicker","widgets/widget"],function(a,b,c){var d=a.$;return d.extend(b.options,{pick:null,accept:null}),b.register({name:"picker",init:function(a){return this.pickers=[],a.pick&&this.addBtn(a.pick)},refresh:function(){d.each(this.pickers,function(){this.refresh()})},addBtn:function(b){var e=this,f=e.options,g=f.accept,h=[];if(b)return d.isPlainObject(b)||(b={id:b}),d(b.id).each(function(){var i,j,k;k=a.Deferred(),i=d.extend({},b,{accept:d.isPlainObject(g)?[g]:g,swf:f.swf,runtimeOrder:f.runtimeOrder,id:this}),j=new c(i),j.once("ready",k.resolve),j.on("select",function(a){e.owner.request("add-file",[a])}),j.init(),e.pickers.push(j),h.push(k.promise())}),a.when.apply(a,h)},disable:function(){d.each(this.pickers,function(){this.disable()})},enable:function(){d.each(this.pickers,function(){this.enable()})},destroy:function(){d.each(this.pickers,function(){this.destroy()}),this.pickers=null}})}),b("lib/image",["base","runtime/client","lib/blob"],function(a,b,c){function d(a){this.options=e.extend({},d.options,a),b.call(this,"Image"),this.on("load",function(){this._info=this.exec("info"),this._meta=this.exec("meta")})}var e=a.$;return d.options={quality:90,crop:!1,preserveHeaders:!1,allowMagnify:!1},a.inherits(b,{constructor:d,info:function(a){return a?(this._info=a,this):this._info},meta:function(a){return a?(this._meta=a,this):this._meta},loadFromBlob:function(a){var b=this,c=a.getRuid();this.connectRuntime(c,function(){b.exec("init",b.options),b.exec("loadFromBlob",a)})},resize:function(){var b=a.slice(arguments);return this.exec.apply(this,["resize"].concat(b))},crop:function(){var b=a.slice(arguments);return this.exec.apply(this,["crop"].concat(b))},getAsDataUrl:function(a){return this.exec("getAsDataUrl",a)},getAsBlob:function(a){var b=this.exec("getAsBlob",a);return new c(this.getRuid(),b)}}),d}),b("widgets/image",["base","uploader","lib/image","widgets/widget"],function(a,b,c){var d,e=a.$;return d=function(a){var b=0,c=[],d=function(){for(var d;c.length&&a>b;)d=c.shift(),b+=d[0],d[1]()};return function(a,e,f){c.push([e,f]),a.once("destroy",function(){b-=e,setTimeout(d,1)}),setTimeout(d,1)}}(5242880),e.extend(b.options,{thumb:{width:110,height:110,quality:70,allowMagnify:!0,crop:!0,preserveHeaders:!1,type:"image/jpeg"},compress:{width:1600,height:1600,quality:90,allowMagnify:!1,crop:!1,preserveHeaders:!0}}),b.register({name:"image",makeThumb:function(a,b,f,g){var h,i;return a=this.request("get-file",a),a.type.match(/^image/)?(h=e.extend({},this.options.thumb),e.isPlainObject(f)&&(h=e.extend(h,f),f=null),f=f||h.width,g=g||h.height,i=new c(h),i.once("load",function(){a._info=a._info||i.info(),a._meta=a._meta||i.meta(),1>=f&&f>0&&(f=a._info.width*f),1>=g&&g>0&&(g=a._info.height*g),i.resize(f,g)}),i.once("complete",function(){b(!1,i.getAsDataUrl(h.type)),i.destroy()}),i.once("error",function(a){b(a||!0),i.destroy()}),void d(i,a.source.size,function(){a._info&&i.info(a._info),a._meta&&i.meta(a._meta),i.loadFromBlob(a.source)})):void b(!0)},beforeSendFile:function(b){var d,f,g=this.options.compress||this.options.resize,h=g&&g.compressSize||0,i=g&&g.noCompressIfLarger||!1;return b=this.request("get-file",b),!g||!~"image/jpeg,image/jpg".indexOf(b.type)||b.size=a&&a>0&&(a=b._info.width*a),1>=c&&c>0&&(c=b._info.height*c),d.resize(a,c)}),d.once("complete",function(){var a,c;try{a=d.getAsBlob(g.type),c=b.size,(!i||a.sizeb;b++)if(c=this._queue[b],a===c.getStatus())return c;return null},sort:function(a){"function"==typeof a&&this._queue.sort(a)},getFiles:function(){for(var a,b=[].slice.call(arguments,0),c=[],d=0,f=this._queue.length;f>d;d++)a=this._queue[d],(!b.length||~e.inArray(a.getStatus(),b))&&c.push(a);return c},removeFile:function(a){var b=this._map[a.id];b&&(delete this._map[a.id],a.destroy(),this.stats.numofDeleted++)},_fileAdded:function(a){var b=this,c=this._map[a.id];c||(this._map[a.id]=a,a.on("statuschange",function(a,c){b._onFileStatusChange(a,c)}))},_onFileStatusChange:function(a,b){var c=this.stats;switch(b){case f.PROGRESS:c.numOfProgress--;break;case f.QUEUED:c.numOfQueue--;break;case f.ERROR:c.numOfUploadFailed--;break;case f.INVALID:c.numOfInvalid--;break;case f.INTERRUPT:c.numofInterrupt--}switch(a){case f.QUEUED:c.numOfQueue++;break;case f.PROGRESS:c.numOfProgress++;break;case f.ERROR:c.numOfUploadFailed++;break;case f.COMPLETE:c.numOfSuccess++;break;case f.CANCELLED:c.numOfCancel++;break;case f.INVALID:c.numOfInvalid++;break;case f.INTERRUPT:c.numofInterrupt++}}}),b.installTo(d.prototype),d}),b("widgets/queue",["base","uploader","queue","file","lib/file","runtime/client","widgets/widget"],function(a,b,c,d,e,f){var g=a.$,h=/\.\w+$/,i=d.Status;return b.register({name:"queue",init:function(b){var d,e,h,i,j,k,l,m=this;if(g.isPlainObject(b.accept)&&(b.accept=[b.accept]),b.accept){for(j=[],h=0,e=b.accept.length;e>h;h++)i=b.accept[h].extensions,i&&j.push(i);j.length&&(k="\\."+j.join(",").replace(/,/g,"$|\\.").replace(/\*/g,".*")+"$"),m.accept=new RegExp(k,"i")}return m.queue=new c,m.stats=m.queue.stats,"html5"===this.request("predict-runtime-type")?(d=a.Deferred(),this.placeholder=l=new f("Placeholder"),l.connectRuntime({runtimeOrder:"html5"},function(){m._ruid=l.getRuid(),d.resolve()}),d.promise()):void 0},_wrapFile:function(a){if(!(a instanceof d)){if(!(a instanceof e)){if(!this._ruid)throw new Error("Can't add external files.");a=new e(this._ruid,a)}a=new d(a)}return a},acceptFile:function(a){var b=!a||!a.size||this.accept&&h.exec(a.name)&&!this.accept.test(a.name);return!b},_addFile:function(a){var b=this;return a=b._wrapFile(a),b.owner.trigger("beforeFileQueued",a)?b.acceptFile(a)?(b.queue.append(a),b.owner.trigger("fileQueued",a),a):void b.owner.trigger("error","Q_TYPE_DENIED",a):void 0},getFile:function(a){return this.queue.getFile(a)},addFile:function(a){var b=this;a.length||(a=[a]),a=g.map(a,function(a){return b._addFile(a)}),b.owner.trigger("filesQueued",a),b.options.auto&&setTimeout(function(){b.request("start-upload")},20)},getStats:function(){return this.stats},removeFile:function(a,b){var c=this;a=a.id?a:c.queue.getFile(a),this.request("cancel-file",a),b&&this.queue.removeFile(a)},getFiles:function(){return this.queue.getFiles.apply(this.queue,arguments)},fetchFile:function(){return this.queue.fetch.apply(this.queue,arguments)},retry:function(a,b){var c,d,e,f=this;if(a)return a=a.id?a:f.queue.getFile(a),a.setStatus(i.QUEUED),void(b||f.request("start-upload"));for(c=f.queue.getFiles(i.ERROR),d=0,e=c.length;e>d;d++)a=c[d],a.setStatus(i.QUEUED);f.request("start-upload")},sortFiles:function(){return this.queue.sort.apply(this.queue,arguments)},reset:function(){this.owner.trigger("reset"),this.queue=new c,this.stats=this.queue.stats},destroy:function(){this.reset(),this.placeholder&&this.placeholder.destroy()}})}),b("widgets/runtime",["uploader","runtime/runtime","widgets/widget"],function(a,b){return a.support=function(){return b.hasRuntime.apply(b,arguments)},a.register({name:"runtime",init:function(){if(!this.predictRuntimeType())throw Error("Runtime Error")},predictRuntimeType:function(){var a,c,d=this.options.runtimeOrder||b.orders,e=this.type;if(!e)for(d=d.split(/\s*,\s*/g),a=0,c=d.length;c>a;a++)if(b.hasRuntime(d[a])){this.type=e=d[a];break}return e}})}),b("lib/transport",["base","runtime/client","mediator"],function(a,b,c){function d(a){var c=this;a=c.options=e.extend(!0,{},d.options,a||{}),b.call(this,"Transport"),this._blob=null,this._formData=a.formData||{},this._headers=a.headers||{},this.on("progress",this._timeout),this.on("load error",function(){c.trigger("progress",1),clearTimeout(c._timer)})}var e=a.$;return d.options={server:"",method:"POST",withCredentials:!1,fileVal:"file",timeout:12e4,formData:{},headers:{},sendAsBinary:!1},e.extend(d.prototype,{appendBlob:function(a,b,c){var d=this,e=d.options;d.getRuid()&&d.disconnectRuntime(),d.connectRuntime(b.ruid,function(){d.exec("init")}),d._blob=b,e.fileVal=a||e.fileVal,e.filename=c||e.filename},append:function(a,b){"object"==typeof a?e.extend(this._formData,a):this._formData[a]=b},setRequestHeader:function(a,b){"object"==typeof a?e.extend(this._headers,a):this._headers[a]=b},send:function(a){this.exec("send",a),this._timeout()},abort:function(){return clearTimeout(this._timer),this.exec("abort")},destroy:function(){this.trigger("destroy"),this.off(),this.exec("destroy"),this.disconnectRuntime()},getResponse:function(){return this.exec("getResponse")},getResponseAsJson:function(){return this.exec("getResponseAsJson")},getStatus:function(){return this.exec("getStatus")},_timeout:function(){var a=this,b=a.options.timeout;b&&(clearTimeout(a._timer),a._timer=setTimeout(function(){a.abort(),a.trigger("error","timeout")},b))}}),c.installTo(d.prototype),d}),b("widgets/upload",["base","uploader","file","lib/transport","widgets/widget"],function(a,b,c,d){function e(a,b){var c,d,e=[],f=a.source,g=f.size,h=b?Math.ceil(g/b):1,i=0,j=0;for(d={file:a,has:function(){return!!e.length},shift:function(){return e.shift()},unshift:function(a){e.unshift(a)}};h>j;)c=Math.min(b,g-i),e.push({file:a,start:i,end:b?i+c:g,total:g,chunks:h,chunk:j++,cuted:d}),i+=c;return a.blocks=e.concat(),a.remaning=e.length,d}var f=a.$,g=a.isPromise,h=c.Status;f.extend(b.options,{prepareNextFile:!1,chunked:!1,chunkSize:5242880,chunkRetry:2,threads:3,formData:{}}),b.register({name:"upload",init:function(){var b=this.owner,c=this;this.runing=!1,this.progress=!1,b.on("startUpload",function(){c.progress=!0}).on("uploadFinished",function(){c.progress=!1}),this.pool=[],this.stack=[],this.pending=[],this.remaning=0,this.__tick=a.bindFn(this._tick,this),b.on("uploadComplete",function(a){a.blocks&&f.each(a.blocks,function(a,b){b.transport&&(b.transport.abort(),b.transport.destroy()),delete b.transport}),delete a.blocks,delete a.remaning})},reset:function(){this.request("stop-upload",!0),this.runing=!1,this.pool=[],this.stack=[],this.pending=[],this.remaning=0,this._trigged=!1,this._promise=null},startUpload:function(b){var c=this;if(f.each(c.request("get-files",h.INVALID),function(){c.request("remove-file",this)}),b)if(b=b.id?b:c.request("get-file",b),b.getStatus()===h.INTERRUPT)f.each(c.pool,function(a,c){c.file===b&&c.transport&&c.transport.send()}),b.setStatus(h.QUEUED);else{if(b.getStatus()===h.PROGRESS)return;b.setStatus(h.QUEUED)}else f.each(c.request("get-files",[h.INITED]),function(){this.setStatus(h.QUEUED)});if(!c.runing){c.runing=!0;var d=[];f.each(c.pool,function(a,b){var e=b.file;e.getStatus()===h.INTERRUPT&&(d.push(e),c._trigged=!1,b.transport&&b.transport.send())});for(var b;b=d.shift();)b.setStatus(h.PROGRESS);b||f.each(c.request("get-files",h.INTERRUPT),function(){this.setStatus(h.PROGRESS)}),c._trigged=!1,a.nextTick(c.__tick),c.owner.trigger("startUpload")}},stopUpload:function(b,c){var d=this;if(b===!0&&(c=b,b=null),d.runing!==!1){if(b){if(b=b.id?b:d.request("get-file",b),b.getStatus()!==h.PROGRESS&&b.getStatus()!==h.QUEUED)return;return b.setStatus(h.INTERRUPT),f.each(d.pool,function(a,c){c.file===b&&(c.transport&&c.transport.abort(),d._putback(c),d._popBlock(c))}),a.nextTick(d.__tick)}d.runing=!1,this._promise&&this._promise.file&&this._promise.file.setStatus(h.INTERRUPT),c&&f.each(d.pool,function(a,b){b.transport&&b.transport.abort(),b.file.setStatus(h.INTERRUPT)}),d.owner.trigger("stopUpload")}},cancelFile:function(a){a=a.id?a:this.request("get-file",a),a.blocks&&f.each(a.blocks,function(a,b){var c=b.transport;c&&(c.abort(),c.destroy(),delete b.transport)}),a.setStatus(h.CANCELLED),this.owner.trigger("fileDequeued",a)},isInProgress:function(){return!!this.progress},_getStats:function(){return this.request("get-stats")},skipFile:function(a,b){a=a.id?a:this.request("get-file",a),a.setStatus(b||h.COMPLETE),a.skipped=!0,a.blocks&&f.each(a.blocks,function(a,b){var c=b.transport;c&&(c.abort(),c.destroy(),delete b.transport)}),this.owner.trigger("uploadSkip",a)},_tick:function(){var b,c,d=this,e=d.options;return d._promise?d._promise.always(d.__tick):void(d.pool.length1&&~"http,abort".indexOf(a)&&b.retried1&&f.extend(m,{chunks:b.chunks,chunk:b.chunk}),i.trigger("uploadBeforeSend",b,m,n),l.appendBlob(j.fileVal,b.blob,k.name),l.append(m),l.setRequestHeader(n),l.send()},_finishFile:function(a,b,c){var d=this.owner;return d.request("after-send-file",arguments,function(){a.setStatus(h.COMPLETE),d.trigger("uploadSuccess",a,b,c)}).fail(function(b){a.getStatus()===h.PROGRESS&&a.setStatus(h.ERROR,b),d.trigger("uploadError",a,b)}).always(function(){d.trigger("uploadComplete",a)})},updateFileProgress:function(a){var b=0,c=0;a.blocks&&(f.each(a.blocks,function(a,b){c+=(b.percentage||0)*(b.end-b.start)}),b=c/a.size,this.owner.trigger("uploadProgress",a,b||0))}})}),b("widgets/validator",["base","uploader","file","widgets/widget"],function(a,b,c){var d,e=a.$,f={};return d={addValidator:function(a,b){f[a]=b},removeValidator:function(a){delete f[a]}},b.register({name:"validator",init:function(){var b=this;a.nextTick(function(){e.each(f,function(){this.call(b.owner)})})}}),d.addValidator("fileNumLimit",function(){var a=this,b=a.options,c=0,d=parseInt(b.fileNumLimit,10),e=!0;d&&(a.on("beforeFileQueued",function(a){return c>=d&&e&&(e=!1,this.trigger("error","Q_EXCEED_NUM_LIMIT",d,a),setTimeout(function(){e=!0},1)),c>=d?!1:!0}),a.on("fileQueued",function(){c++}),a.on("fileDequeued",function(){c--}),a.on("reset",function(){c=0}))}),d.addValidator("fileSizeLimit",function(){var a=this,b=a.options,c=0,d=parseInt(b.fileSizeLimit,10),e=!0;d&&(a.on("beforeFileQueued",function(a){var b=c+a.size>d;return b&&e&&(e=!1,this.trigger("error","Q_EXCEED_SIZE_LIMIT",d,a),setTimeout(function(){e=!0},1)),b?!1:!0}),a.on("fileQueued",function(a){c+=a.size}),a.on("fileDequeued",function(a){c-=a.size}),a.on("reset",function(){c=0}))}),d.addValidator("fileSingleSizeLimit",function(){var a=this,b=a.options,d=b.fileSingleSizeLimit;d&&a.on("beforeFileQueued",function(a){return a.size>d?(a.setStatus(c.Status.INVALID,"exceed_size"),this.trigger("error","F_EXCEED_SIZE",d,a),!1):void 0})}),d.addValidator("duplicate",function(){function a(a){for(var b,c=0,d=0,e=a.length;e>d;d++)b=a.charCodeAt(d),c=b+(c<<6)+(c<<16)-c;return c}var b=this,c=b.options,d={};c.duplicate||(b.on("beforeFileQueued",function(b){var c=b.__hash||(b.__hash=a(b.name+b.size+b.lastModifiedDate)); 2 | return d[c]?(this.trigger("error","F_DUPLICATE",b),!1):void 0}),b.on("fileQueued",function(a){var b=a.__hash;b&&(d[b]=!0)}),b.on("fileDequeued",function(a){var b=a.__hash;b&&delete d[b]}),b.on("reset",function(){d={}}))}),d}),b("runtime/compbase",[],function(){function a(a,b){this.owner=a,this.options=a.options,this.getRuntime=function(){return b},this.getRuid=function(){return b.uid},this.trigger=function(){return a.trigger.apply(a,arguments)}}return a}),b("runtime/flash/runtime",["base","runtime/runtime","runtime/compbase"],function(b,c,d){function e(){var a;try{a=navigator.plugins["Shockwave Flash"],a=a.description}catch(b){try{a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version")}catch(c){a="0.0"}}return a=a.match(/\d+/g),parseFloat(a[0]+"."+a[1],10)}function f(){function d(a,b){var c,d,e=a.type||a;c=e.split("::"),d=c[0],e=c[1],"Ready"===e&&d===j.uid?j.trigger("ready"):f[d]&&f[d].trigger(e.toLowerCase(),a,b)}var e={},f={},g=this.destroy,j=this,k=b.guid("webuploader_");c.apply(j,arguments),j.type=h,j.exec=function(a,c){var d,g=this,h=g.uid,k=b.slice(arguments,2);return f[h]=g,i[a]&&(e[h]||(e[h]=new i[a](g,j)),d=e[h],d[c])?d[c].apply(d,k):j.flashExec.apply(g,arguments)},a[k]=function(){var a=arguments;setTimeout(function(){d.apply(null,a)},1)},this.jsreciver=k,this.destroy=function(){return g&&g.apply(this,arguments)},this.flashExec=function(a,c){var d=j.getFlash(),e=b.slice(arguments,2);return d.exec(this.uid,a,c,e)}}var g=b.$,h="flash",i={};return b.inherits(c,{constructor:f,init:function(){var a,c=this.getContainer(),d=this.options;c.css({position:"absolute",top:"-8px",left:"-8px",width:"9px",height:"9px",overflow:"hidden"}),a='',c.html(a)},getFlash:function(){return this._flash?this._flash:(this._flash=g("#"+this.uid).get(0),this._flash)}}),f.register=function(a,c){return c=i[a]=b.inherits(d,g.extend({flashExec:function(){var a=this.owner,b=this.getRuntime();return b.flashExec.apply(a,arguments)}},c))},e()>=11.4&&c.addRuntime(h,f),f}),b("runtime/flash/filepicker",["base","runtime/flash/runtime"],function(a,b){var c=a.$;return b.register("FilePicker",{init:function(a){var b,d,e=c.extend({},a);for(b=e.accept&&e.accept.length,d=0;b>d;d++)e.accept[d].title||(e.accept[d].title="Files");delete e.button,delete e.id,delete e.container,this.flashExec("FilePicker","init",e)},destroy:function(){this.flashExec("FilePicker","destroy")}})}),b("runtime/flash/image",["runtime/flash/runtime"],function(a){return a.register("Image",{loadFromBlob:function(a){var b=this.owner;b.info()&&this.flashExec("Image","info",b.info()),b.meta()&&this.flashExec("Image","meta",b.meta()),this.flashExec("Image","loadFromBlob",a.uid)}})}),b("runtime/flash/blob",["runtime/flash/runtime","lib/blob"],function(a,b){return a.register("Blob",{slice:function(a,c){var d=this.flashExec("Blob","slice",a,c);return new b(d.uid,d)}})}),b("runtime/flash/transport",["base","runtime/flash/runtime","runtime/client"],function(b,c,d){var e=b.$;return c.register("Transport",{init:function(){this._status=0,this._response=null,this._responseJson=null},send:function(){var a,b=this.owner,c=this.options,d=this._initAjax(),f=b._blob,g=c.server;d.connectRuntime(f.ruid),c.sendAsBinary?(g+=(/\?/.test(g)?"&":"?")+e.param(b._formData),a=f.uid):(e.each(b._formData,function(a,b){d.exec("append",a,b)}),d.exec("appendBlob",c.fileVal,f.uid,c.filename||b._formData.name||"")),this._setRequestHeader(d,c.headers),d.exec("send",{method:c.method,url:g,forceURLStream:c.forceURLStream,mimeType:"application/octet-stream"},a)},getStatus:function(){return this._status},getResponse:function(){return this._response||""},getResponseAsJson:function(){return this._responseJson},abort:function(){var a=this._xhr;a&&(a.exec("abort"),a.destroy(),this._xhr=a=null)},destroy:function(){this.abort()},_initAjax:function(){var b=this,c=new d("XMLHttpRequest");return c.on("uploadprogress progress",function(a){var c=a.loaded/a.total;return c=Math.min(1,Math.max(0,c)),b.trigger("progress",c)}),c.on("load",function(){var d,e=c.exec("getStatus"),f=!1,g="";return c.off(),b._xhr=null,e>=200&&300>e?f=!0:e>=500&&600>e?(f=!0,g="server"):g="http",f&&(b._response=c.exec("getResponse"),b._response=decodeURIComponent(b._response),d=a.JSON&&a.JSON.parse||function(a){try{return new Function("return "+a).call()}catch(b){return{}}},b._responseJson=b._response?d(b._response):{}),c.destroy(),c=null,g?b.trigger("error",g):b.trigger("load")}),c.on("error",function(){c.off(),b._xhr=null,b.trigger("error","http")}),b._xhr=c,c},_setRequestHeader:function(a,b){e.each(b,function(b,c){a.exec("setRequestHeader",b,c)})}})}),b("preset/flashonly",["base","widgets/filepicker","widgets/image","widgets/queue","widgets/runtime","widgets/upload","widgets/validator","runtime/flash/filepicker","runtime/flash/image","runtime/flash/blob","runtime/flash/transport"],function(a){return a}),b("webuploader",["preset/flashonly"],function(a){return a}),c("webuploader")}); -------------------------------------------------------------------------------- /webuploader.withoutimage.min.js: -------------------------------------------------------------------------------- 1 | /* WebUploader 0.1.5 */!function(a,b){var c,d={},e=function(a,b){var c,d,e;if("string"==typeof a)return h(a);for(c=[],d=a.length,e=0;d>e;e++)c.push(h(a[e]));return b.apply(null,c)},f=function(a,b,c){2===arguments.length&&(c=b,b=null),e(b||[],function(){g(a,c,arguments)})},g=function(a,b,c){var f,g={exports:b};"function"==typeof b&&(c.length||(c=[e,g.exports,g]),f=b.apply(null,c),void 0!==f&&(g.exports=f)),d[a]=g.exports},h=function(b){var c=d[b]||a[b];if(!c)throw new Error("`"+b+"` is undefined");return c},i=function(a){var b,c,e,f,g,h;h=function(a){return a&&a.charAt(0).toUpperCase()+a.substr(1)};for(b in d)if(c=a,d.hasOwnProperty(b)){for(e=b.split("/"),g=h(e.pop());f=h(e.shift());)c[f]=c[f]||{},c=c[f];c[g]=d[b]}return a},j=function(c){return a.__dollar=c,i(b(a,f,e))};"object"==typeof module&&"object"==typeof module.exports?module.exports=j():"function"==typeof define&&define.amd?define(["jquery"],j):(c=a.WebUploader,a.WebUploader=j(),a.WebUploader.noConflict=function(){a.WebUploader=c})}(window,function(a,b,c){return b("dollar-third",[],function(){var b=a.__dollar||a.jQuery||a.Zepto;if(!b)throw new Error("jQuery or Zepto not found!");return b}),b("dollar",["dollar-third"],function(a){return a}),b("promise-third",["dollar"],function(a){return{Deferred:a.Deferred,when:a.when,isPromise:function(a){return a&&"function"==typeof a.then}}}),b("promise",["promise-third"],function(a){return a}),b("base",["dollar","promise"],function(b,c){function d(a){return function(){return h.apply(a,arguments)}}function e(a,b){return function(){return a.apply(b,arguments)}}function f(a){var b;return Object.create?Object.create(a):(b=function(){},b.prototype=a,new b)}var g=function(){},h=Function.call;return{version:"0.1.5",$:b,Deferred:c.Deferred,isPromise:c.isPromise,when:c.when,browser:function(a){var b={},c=a.match(/WebKit\/([\d.]+)/),d=a.match(/Chrome\/([\d.]+)/)||a.match(/CriOS\/([\d.]+)/),e=a.match(/MSIE\s([\d\.]+)/)||a.match(/(?:trident)(?:.*rv:([\w.]+))?/i),f=a.match(/Firefox\/([\d.]+)/),g=a.match(/Safari\/([\d.]+)/),h=a.match(/OPR\/([\d.]+)/);return c&&(b.webkit=parseFloat(c[1])),d&&(b.chrome=parseFloat(d[1])),e&&(b.ie=parseFloat(e[1])),f&&(b.firefox=parseFloat(f[1])),g&&(b.safari=parseFloat(g[1])),h&&(b.opera=parseFloat(h[1])),b}(navigator.userAgent),os:function(a){var b={},c=a.match(/(?:Android);?[\s\/]+([\d.]+)?/),d=a.match(/(?:iPad|iPod|iPhone).*OS\s([\d_]+)/);return c&&(b.android=parseFloat(c[1])),d&&(b.ios=parseFloat(d[1].replace(/_/g,"."))),b}(navigator.userAgent),inherits:function(a,c,d){var e;return"function"==typeof c?(e=c,c=null):e=c&&c.hasOwnProperty("constructor")?c.constructor:function(){return a.apply(this,arguments)},b.extend(!0,e,a,d||{}),e.__super__=a.prototype,e.prototype=f(a.prototype),c&&b.extend(!0,e.prototype,c),e},noop:g,bindFn:e,log:function(){return a.console?e(console.log,console):g}(),nextTick:function(){return function(a){setTimeout(a,1)}}(),slice:d([].slice),guid:function(){var a=0;return function(b){for(var c=(+new Date).toString(32),d=0;5>d;d++)c+=Math.floor(65535*Math.random()).toString(32);return(b||"wu_")+c+(a++).toString(32)}}(),formatSize:function(a,b,c){var d;for(c=c||["B","K","M","G","TB"];(d=c.shift())&&a>1024;)a/=1024;return("B"===d?a:a.toFixed(b||2))+d}}}),b("mediator",["base"],function(a){function b(a,b,c,d){return f.grep(a,function(a){return!(!a||b&&a.e!==b||c&&a.cb!==c&&a.cb._cb!==c||d&&a.ctx!==d)})}function c(a,b,c){f.each((a||"").split(h),function(a,d){c(d,b)})}function d(a,b){for(var c,d=!1,e=-1,f=a.length;++e1?void(d.isPlainObject(b)&&d.isPlainObject(c[a])?d.extend(c[a],b):c[a]=b):a?c[a]:c},getStats:function(){var a=this.request("get-stats");return a?{successNum:a.numOfSuccess,progressNum:a.numOfProgress,cancelNum:a.numOfCancel,invalidNum:a.numOfInvalid,uploadFailNum:a.numOfUploadFailed,queueNum:a.numOfQueue,interruptNum:a.numofInterrupt}:{}},trigger:function(a){var c=[].slice.call(arguments,1),e=this.options,f="on"+a.substring(0,1).toUpperCase()+a.substring(1);return b.trigger.apply(this,arguments)===!1||d.isFunction(e[f])&&e[f].apply(this,c)===!1||d.isFunction(this[f])&&this[f].apply(this,c)===!1||b.trigger.apply(b,[this,a].concat(c))===!1?!1:!0},destroy:function(){this.request("destroy",arguments),this.off()},request:a.noop}),a.create=c.create=function(a){return new c(a)},a.Uploader=c,c}),b("runtime/runtime",["base","mediator"],function(a,b){function c(b){this.options=d.extend({container:document.body},b),this.uid=a.guid("rt_")}var d=a.$,e={},f=function(a){for(var b in a)if(a.hasOwnProperty(b))return b;return null};return d.extend(c.prototype,{getContainer:function(){var a,b,c=this.options;return this._container?this._container:(a=d(c.container||document.body),b=d(document.createElement("div")),b.attr("id","rt_"+this.uid),b.css({position:"absolute",top:"0px",left:"0px",width:"1px",height:"1px",overflow:"hidden"}),a.append(b),a.addClass("webuploader-container"),this._container=b,this._parent=a,b)},init:a.noop,exec:a.noop,destroy:function(){this._container&&this._container.remove(),this._parent&&this._parent.removeClass("webuploader-container"),this.off()}}),c.orders="html5,flash",c.addRuntime=function(a,b){e[a]=b},c.hasRuntime=function(a){return!!(a?e[a]:f(e))},c.create=function(a,b){var g,h;if(b=b||c.orders,d.each(b.split(/\s*,\s*/g),function(){return e[this]?(g=this,!1):void 0}),g=g||f(e),!g)throw new Error("Runtime Error");return h=new e[g](a)},b.installTo(c.prototype),c}),b("runtime/client",["base","mediator","runtime/runtime"],function(a,b,c){function d(b,d){var f,g=a.Deferred();this.uid=a.guid("client_"),this.runtimeReady=function(a){return g.done(a)},this.connectRuntime=function(b,h){if(f)throw new Error("already connected!");return g.done(h),"string"==typeof b&&e.get(b)&&(f=e.get(b)),f=f||e.get(null,d),f?(a.$.extend(f.options,b),f.__promise.then(g.resolve),f.__client++):(f=c.create(b,b.runtimeOrder),f.__promise=g.promise(),f.once("ready",g.resolve),f.init(),e.add(f),f.__client=1),d&&(f.__standalone=d),f},this.getRuntime=function(){return f},this.disconnectRuntime=function(){f&&(f.__client--,f.__client<=0&&(e.remove(f),delete f.__promise,f.destroy()),f=null)},this.exec=function(){if(f){var c=a.slice(arguments);return b&&c.unshift(b),f.exec.apply(this,c)}},this.getRuid=function(){return f&&f.uid},this.destroy=function(a){return function(){a&&a.apply(this,arguments),this.trigger("destroy"),this.off(),this.exec("destroy"),this.disconnectRuntime()}}(this.destroy)}var e;return e=function(){var a={};return{add:function(b){a[b.uid]=b},get:function(b,c){var d;if(b)return a[b];for(d in a)if(!c||!a[d].__standalone)return a[d];return null},remove:function(b){delete a[b.uid]}}}(),b.installTo(d.prototype),d}),b("lib/dnd",["base","mediator","runtime/client"],function(a,b,c){function d(a){a=this.options=e.extend({},d.options,a),a.container=e(a.container),a.container.length&&c.call(this,"DragAndDrop")}var e=a.$;return d.options={accept:null,disableGlobalDnd:!1},a.inherits(c,{constructor:d,init:function(){var a=this;a.connectRuntime(a.options,function(){a.exec("init"),a.trigger("ready")})}}),b.installTo(d.prototype),d}),b("widgets/widget",["base","uploader"],function(a,b){function c(a){if(!a)return!1;var b=a.length,c=e.type(a);return 1===a.nodeType&&b?!0:"array"===c||"function"!==c&&"string"!==c&&(0===b||"number"==typeof b&&b>0&&b-1 in a)}function d(a){this.owner=a,this.options=a.options}var e=a.$,f=b.prototype._init,g=b.prototype.destroy,h={},i=[];return e.extend(d.prototype,{init:a.noop,invoke:function(a,b){var c=this.responseMap;return c&&a in c&&c[a]in this&&e.isFunction(this[c[a]])?this[c[a]].apply(this,b):h},request:function(){return this.owner.request.apply(this.owner,arguments)}}),e.extend(b.prototype,{_init:function(){var a=this,b=a._widgets=[],c=a.options.disableWidgets||"";return e.each(i,function(d,e){(!c||!~c.indexOf(e._name))&&b.push(new e(a))}),f.apply(a,arguments)},request:function(b,d,e){var f,g,i,j,k=0,l=this._widgets,m=l&&l.length,n=[],o=[];for(d=c(d)?d:[d];m>k;k++)f=l[k],g=f.invoke(b,d),g!==h&&(a.isPromise(g)?o.push(g):n.push(g));return e||o.length?(i=a.when.apply(a,o),j=i.pipe?"pipe":"then",i[j](function(){var b=a.Deferred(),c=arguments;return 1===c.length&&(c=c[0]),setTimeout(function(){b.resolve(c)},1),b.promise()})[e?j:"done"](e||a.noop)):n[0]},destroy:function(){g.apply(this,arguments),this._widgets=null}}),b.register=d.register=function(b,c){var f,g={init:"init",destroy:"destroy",name:"anonymous"};return 1===arguments.length?(c=b,e.each(c,function(a){return"_"===a[0]||"name"===a?void("name"===a&&(g.name=c.name)):void(g[a.replace(/[A-Z]/g,"-$&").toLowerCase()]=a)})):g=e.extend(g,b),c.responseMap=g,f=a.inherits(d,c),f._name=g.name,i.push(f),f},b.unRegister=d.unRegister=function(a){if(a&&"anonymous"!==a)for(var b=i.length;b--;)i[b]._name===a&&i.splice(b,1)},d}),b("widgets/filednd",["base","uploader","lib/dnd","widgets/widget"],function(a,b,c){var d=a.$;return b.options.dnd="",b.register({name:"dnd",init:function(b){if(b.dnd&&"html5"===this.request("predict-runtime-type")){var e,f=this,g=a.Deferred(),h=d.extend({},{disableGlobalDnd:b.disableGlobalDnd,container:b.dnd,accept:b.accept});return this.dnd=e=new c(h),e.once("ready",g.resolve),e.on("drop",function(a){f.request("add-file",[a])}),e.on("accept",function(a){return f.owner.trigger("dndAccept",a)}),e.init(),g.promise()}},destroy:function(){this.dnd&&this.dnd.destroy()}})}),b("lib/filepaste",["base","mediator","runtime/client"],function(a,b,c){function d(a){a=this.options=e.extend({},a),a.container=e(a.container||document.body),c.call(this,"FilePaste")}var e=a.$;return a.inherits(c,{constructor:d,init:function(){var a=this;a.connectRuntime(a.options,function(){a.exec("init"),a.trigger("ready")})}}),b.installTo(d.prototype),d}),b("widgets/filepaste",["base","uploader","lib/filepaste","widgets/widget"],function(a,b,c){var d=a.$;return b.register({name:"paste",init:function(b){if(b.paste&&"html5"===this.request("predict-runtime-type")){var e,f=this,g=a.Deferred(),h=d.extend({},{container:b.paste,accept:b.accept});return this.paste=e=new c(h),e.once("ready",g.resolve),e.on("paste",function(a){f.owner.request("add-file",[a])}),e.init(),g.promise()}},destroy:function(){this.paste&&this.paste.destroy()}})}),b("lib/blob",["base","runtime/client"],function(a,b){function c(a,c){var d=this;d.source=c,d.ruid=a,this.size=c.size||0,this.type=!c.type&&this.ext&&~"jpg,jpeg,png,gif,bmp".indexOf(this.ext)?"image/"+("jpg"===this.ext?"jpeg":this.ext):c.type||"application/octet-stream",b.call(d,"Blob"),this.uid=c.uid||this.uid,a&&d.connectRuntime(a)}return a.inherits(b,{constructor:c,slice:function(a,b){return this.exec("slice",a,b)},getSource:function(){return this.source}}),c}),b("lib/file",["base","lib/blob"],function(a,b){function c(a,c){var f;this.name=c.name||"untitled"+d++,f=e.exec(c.name)?RegExp.$1.toLowerCase():"",!f&&c.type&&(f=/\/(jpg|jpeg|png|gif|bmp)$/i.exec(c.type)?RegExp.$1.toLowerCase():"",this.name+="."+f),this.ext=f,this.lastModifiedDate=c.lastModifiedDate||(new Date).toLocaleString(),b.apply(this,arguments)}var d=1,e=/\.([^.]+)$/;return a.inherits(b,c)}),b("lib/filepicker",["base","runtime/client","lib/file"],function(b,c,d){function e(a){if(a=this.options=f.extend({},e.options,a),a.container=f(a.id),!a.container.length)throw new Error("按钮指定错误");a.innerHTML=a.innerHTML||a.label||a.container.html()||"",a.button=f(a.button||document.createElement("div")),a.button.html(a.innerHTML),a.container.html(a.button),c.call(this,"FilePicker",!0)}var f=b.$;return e.options={button:null,container:null,label:null,innerHTML:null,multiple:!0,accept:null,name:"file"},b.inherits(c,{constructor:e,init:function(){var c=this,e=c.options,g=e.button;g.addClass("webuploader-pick"),c.on("all",function(a){var b;switch(a){case"mouseenter":g.addClass("webuploader-pick-hover");break;case"mouseleave":g.removeClass("webuploader-pick-hover");break;case"change":b=c.exec("getFiles"),c.trigger("select",f.map(b,function(a){return a=new d(c.getRuid(),a),a._refer=e.container,a}),e.container)}}),c.connectRuntime(e,function(){c.refresh(),c.exec("init",e),c.trigger("ready")}),this._resizeHandler=b.bindFn(this.refresh,this),f(a).on("resize",this._resizeHandler)},refresh:function(){var a=this.getRuntime().getContainer(),b=this.options.button,c=b.outerWidth?b.outerWidth():b.width(),d=b.outerHeight?b.outerHeight():b.height(),e=b.offset();c&&d&&a.css({bottom:"auto",right:"auto",width:c+"px",height:d+"px"}).offset(e)},enable:function(){var a=this.options.button;a.removeClass("webuploader-pick-disable"),this.refresh()},disable:function(){var a=this.options.button;this.getRuntime().getContainer().css({top:"-99999px"}),a.addClass("webuploader-pick-disable")},destroy:function(){var b=this.options.button;f(a).off("resize",this._resizeHandler),b.removeClass("webuploader-pick-disable webuploader-pick-hover webuploader-pick")}}),e}),b("widgets/filepicker",["base","uploader","lib/filepicker","widgets/widget"],function(a,b,c){var d=a.$;return d.extend(b.options,{pick:null,accept:null}),b.register({name:"picker",init:function(a){return this.pickers=[],a.pick&&this.addBtn(a.pick)},refresh:function(){d.each(this.pickers,function(){this.refresh()})},addBtn:function(b){var e=this,f=e.options,g=f.accept,h=[];if(b)return d.isPlainObject(b)||(b={id:b}),d(b.id).each(function(){var i,j,k;k=a.Deferred(),i=d.extend({},b,{accept:d.isPlainObject(g)?[g]:g,swf:f.swf,runtimeOrder:f.runtimeOrder,id:this}),j=new c(i),j.once("ready",k.resolve),j.on("select",function(a){e.owner.request("add-file",[a])}),j.init(),e.pickers.push(j),h.push(k.promise())}),a.when.apply(a,h)},disable:function(){d.each(this.pickers,function(){this.disable()})},enable:function(){d.each(this.pickers,function(){this.enable()})},destroy:function(){d.each(this.pickers,function(){this.destroy()}),this.pickers=null}})}),b("file",["base","mediator"],function(a,b){function c(){return f+g++}function d(a){this.name=a.name||"Untitled",this.size=a.size||0,this.type=a.type||"application/octet-stream",this.lastModifiedDate=a.lastModifiedDate||1*new Date,this.id=c(),this.ext=h.exec(this.name)?RegExp.$1:"",this.statusText="",i[this.id]=d.Status.INITED,this.source=a,this.loaded=0,this.on("error",function(a){this.setStatus(d.Status.ERROR,a)})}var e=a.$,f="WU_FILE_",g=0,h=/\.([^.]+)$/,i={};return e.extend(d.prototype,{setStatus:function(a,b){var c=i[this.id];"undefined"!=typeof b&&(this.statusText=b),a!==c&&(i[this.id]=a,this.trigger("statuschange",a,c))},getStatus:function(){return i[this.id]},getSource:function(){return this.source},destroy:function(){this.off(),delete i[this.id]}}),b.installTo(d.prototype),d.Status={INITED:"inited",QUEUED:"queued",PROGRESS:"progress",ERROR:"error",COMPLETE:"complete",CANCELLED:"cancelled",INTERRUPT:"interrupt",INVALID:"invalid"},d}),b("queue",["base","mediator","file"],function(a,b,c){function d(){this.stats={numOfQueue:0,numOfSuccess:0,numOfCancel:0,numOfProgress:0,numOfUploadFailed:0,numOfInvalid:0,numofDeleted:0,numofInterrupt:0},this._queue=[],this._map={}}var e=a.$,f=c.Status;return e.extend(d.prototype,{append:function(a){return this._queue.push(a),this._fileAdded(a),this},prepend:function(a){return this._queue.unshift(a),this._fileAdded(a),this},getFile:function(a){return"string"!=typeof a?a:this._map[a]},fetch:function(a){var b,c,d=this._queue.length;for(a=a||f.QUEUED,b=0;d>b;b++)if(c=this._queue[b],a===c.getStatus())return c;return null},sort:function(a){"function"==typeof a&&this._queue.sort(a)},getFiles:function(){for(var a,b=[].slice.call(arguments,0),c=[],d=0,f=this._queue.length;f>d;d++)a=this._queue[d],(!b.length||~e.inArray(a.getStatus(),b))&&c.push(a);return c},removeFile:function(a){var b=this._map[a.id];b&&(delete this._map[a.id],a.destroy(),this.stats.numofDeleted++)},_fileAdded:function(a){var b=this,c=this._map[a.id];c||(this._map[a.id]=a,a.on("statuschange",function(a,c){b._onFileStatusChange(a,c)}))},_onFileStatusChange:function(a,b){var c=this.stats;switch(b){case f.PROGRESS:c.numOfProgress--;break;case f.QUEUED:c.numOfQueue--;break;case f.ERROR:c.numOfUploadFailed--;break;case f.INVALID:c.numOfInvalid--;break;case f.INTERRUPT:c.numofInterrupt--}switch(a){case f.QUEUED:c.numOfQueue++;break;case f.PROGRESS:c.numOfProgress++;break;case f.ERROR:c.numOfUploadFailed++;break;case f.COMPLETE:c.numOfSuccess++;break;case f.CANCELLED:c.numOfCancel++;break;case f.INVALID:c.numOfInvalid++;break;case f.INTERRUPT:c.numofInterrupt++}}}),b.installTo(d.prototype),d}),b("widgets/queue",["base","uploader","queue","file","lib/file","runtime/client","widgets/widget"],function(a,b,c,d,e,f){var g=a.$,h=/\.\w+$/,i=d.Status;return b.register({name:"queue",init:function(b){var d,e,h,i,j,k,l,m=this;if(g.isPlainObject(b.accept)&&(b.accept=[b.accept]),b.accept){for(j=[],h=0,e=b.accept.length;e>h;h++)i=b.accept[h].extensions,i&&j.push(i);j.length&&(k="\\."+j.join(",").replace(/,/g,"$|\\.").replace(/\*/g,".*")+"$"),m.accept=new RegExp(k,"i")}return m.queue=new c,m.stats=m.queue.stats,"html5"===this.request("predict-runtime-type")?(d=a.Deferred(),this.placeholder=l=new f("Placeholder"),l.connectRuntime({runtimeOrder:"html5"},function(){m._ruid=l.getRuid(),d.resolve()}),d.promise()):void 0},_wrapFile:function(a){if(!(a instanceof d)){if(!(a instanceof e)){if(!this._ruid)throw new Error("Can't add external files.");a=new e(this._ruid,a)}a=new d(a)}return a},acceptFile:function(a){var b=!a||!a.size||this.accept&&h.exec(a.name)&&!this.accept.test(a.name);return!b},_addFile:function(a){var b=this;return a=b._wrapFile(a),b.owner.trigger("beforeFileQueued",a)?b.acceptFile(a)?(b.queue.append(a),b.owner.trigger("fileQueued",a),a):void b.owner.trigger("error","Q_TYPE_DENIED",a):void 0},getFile:function(a){return this.queue.getFile(a)},addFile:function(a){var b=this;a.length||(a=[a]),a=g.map(a,function(a){return b._addFile(a)}),b.owner.trigger("filesQueued",a),b.options.auto&&setTimeout(function(){b.request("start-upload")},20)},getStats:function(){return this.stats},removeFile:function(a,b){var c=this;a=a.id?a:c.queue.getFile(a),this.request("cancel-file",a),b&&this.queue.removeFile(a)},getFiles:function(){return this.queue.getFiles.apply(this.queue,arguments)},fetchFile:function(){return this.queue.fetch.apply(this.queue,arguments)},retry:function(a,b){var c,d,e,f=this;if(a)return a=a.id?a:f.queue.getFile(a),a.setStatus(i.QUEUED),void(b||f.request("start-upload"));for(c=f.queue.getFiles(i.ERROR),d=0,e=c.length;e>d;d++)a=c[d],a.setStatus(i.QUEUED);f.request("start-upload")},sortFiles:function(){return this.queue.sort.apply(this.queue,arguments)},reset:function(){this.owner.trigger("reset"),this.queue=new c,this.stats=this.queue.stats},destroy:function(){this.reset(),this.placeholder&&this.placeholder.destroy()}})}),b("widgets/runtime",["uploader","runtime/runtime","widgets/widget"],function(a,b){return a.support=function(){return b.hasRuntime.apply(b,arguments)},a.register({name:"runtime",init:function(){if(!this.predictRuntimeType())throw Error("Runtime Error")},predictRuntimeType:function(){var a,c,d=this.options.runtimeOrder||b.orders,e=this.type;if(!e)for(d=d.split(/\s*,\s*/g),a=0,c=d.length;c>a;a++)if(b.hasRuntime(d[a])){this.type=e=d[a];break}return e}})}),b("lib/transport",["base","runtime/client","mediator"],function(a,b,c){function d(a){var c=this;a=c.options=e.extend(!0,{},d.options,a||{}),b.call(this,"Transport"),this._blob=null,this._formData=a.formData||{},this._headers=a.headers||{},this.on("progress",this._timeout),this.on("load error",function(){c.trigger("progress",1),clearTimeout(c._timer)})}var e=a.$;return d.options={server:"",method:"POST",withCredentials:!1,fileVal:"file",timeout:12e4,formData:{},headers:{},sendAsBinary:!1},e.extend(d.prototype,{appendBlob:function(a,b,c){var d=this,e=d.options;d.getRuid()&&d.disconnectRuntime(),d.connectRuntime(b.ruid,function(){d.exec("init")}),d._blob=b,e.fileVal=a||e.fileVal,e.filename=c||e.filename},append:function(a,b){"object"==typeof a?e.extend(this._formData,a):this._formData[a]=b},setRequestHeader:function(a,b){"object"==typeof a?e.extend(this._headers,a):this._headers[a]=b},send:function(a){this.exec("send",a),this._timeout()},abort:function(){return clearTimeout(this._timer),this.exec("abort")},destroy:function(){this.trigger("destroy"),this.off(),this.exec("destroy"),this.disconnectRuntime()},getResponse:function(){return this.exec("getResponse")},getResponseAsJson:function(){return this.exec("getResponseAsJson")},getStatus:function(){return this.exec("getStatus")},_timeout:function(){var a=this,b=a.options.timeout;b&&(clearTimeout(a._timer),a._timer=setTimeout(function(){a.abort(),a.trigger("error","timeout")},b))}}),c.installTo(d.prototype),d}),b("widgets/upload",["base","uploader","file","lib/transport","widgets/widget"],function(a,b,c,d){function e(a,b){var c,d,e=[],f=a.source,g=f.size,h=b?Math.ceil(g/b):1,i=0,j=0;for(d={file:a,has:function(){return!!e.length},shift:function(){return e.shift()},unshift:function(a){e.unshift(a)}};h>j;)c=Math.min(b,g-i),e.push({file:a,start:i,end:b?i+c:g,total:g,chunks:h,chunk:j++,cuted:d}),i+=c;return a.blocks=e.concat(),a.remaning=e.length,d}var f=a.$,g=a.isPromise,h=c.Status;f.extend(b.options,{prepareNextFile:!1,chunked:!1,chunkSize:5242880,chunkRetry:2,threads:3,formData:{}}),b.register({name:"upload",init:function(){var b=this.owner,c=this;this.runing=!1,this.progress=!1,b.on("startUpload",function(){c.progress=!0}).on("uploadFinished",function(){c.progress=!1}),this.pool=[],this.stack=[],this.pending=[],this.remaning=0,this.__tick=a.bindFn(this._tick,this),b.on("uploadComplete",function(a){a.blocks&&f.each(a.blocks,function(a,b){b.transport&&(b.transport.abort(),b.transport.destroy()),delete b.transport}),delete a.blocks,delete a.remaning})},reset:function(){this.request("stop-upload",!0),this.runing=!1,this.pool=[],this.stack=[],this.pending=[],this.remaning=0,this._trigged=!1,this._promise=null},startUpload:function(b){var c=this;if(f.each(c.request("get-files",h.INVALID),function(){c.request("remove-file",this)}),b)if(b=b.id?b:c.request("get-file",b),b.getStatus()===h.INTERRUPT)f.each(c.pool,function(a,c){c.file===b&&c.transport&&c.transport.send()}),b.setStatus(h.QUEUED);else{if(b.getStatus()===h.PROGRESS)return;b.setStatus(h.QUEUED)}else f.each(c.request("get-files",[h.INITED]),function(){this.setStatus(h.QUEUED)});c.runing||(c.runing=!0,f.each(c.pool,function(a,b){var d=b.file;d.getStatus()===h.INTERRUPT&&(d.setStatus(h.PROGRESS),c._trigged=!1,b.transport&&b.transport.send())}),b||f.each(c.request("get-files",h.INTERRUPT),function(){this.setStatus(h.PROGRESS)}),c._trigged=!1,a.nextTick(c.__tick),c.owner.trigger("startUpload"))},stopUpload:function(b,c){var d=this;if(b===!0&&(c=b,b=null),d.runing!==!1){if(b){if(b=b.id?b:d.request("get-file",b),b.getStatus()!==h.PROGRESS&&b.getStatus()!==h.QUEUED)return;return b.setStatus(h.INTERRUPT),f.each(d.pool,function(a,c){c.file===b&&(c.transport&&c.transport.abort(),d._putback(c),d._popBlock(c))}),a.nextTick(d.__tick)}d.runing=!1,this._promise&&this._promise.file&&this._promise.file.setStatus(h.INTERRUPT),c&&f.each(d.pool,function(a,b){b.transport&&b.transport.abort(),b.file.setStatus(h.INTERRUPT)}),d.owner.trigger("stopUpload")}},cancelFile:function(a){a=a.id?a:this.request("get-file",a),a.blocks&&f.each(a.blocks,function(a,b){var c=b.transport;c&&(c.abort(),c.destroy(),delete b.transport)}),a.setStatus(h.CANCELLED),this.owner.trigger("fileDequeued",a)},isInProgress:function(){return!!this.progress},_getStats:function(){return this.request("get-stats")},skipFile:function(a,b){a=a.id?a:this.request("get-file",a),a.setStatus(b||h.COMPLETE),a.skipped=!0,a.blocks&&f.each(a.blocks,function(a,b){var c=b.transport;c&&(c.abort(),c.destroy(),delete b.transport)}),this.owner.trigger("uploadSkip",a)},_tick:function(){var b,c,d=this,e=d.options;return d._promise?d._promise.always(d.__tick):void(d.pool.length1&&(f.each(k.blocks,function(a,b){d+=(b.percentage||0)*(b.end-b.start)}),c=d/k.size),i.trigger("uploadProgress",k,c||0)}),c=function(a){var c;return e=l.getResponseAsJson()||{},e._raw=l.getResponse(),c=function(b){a=b},i.trigger("uploadAccept",b,e,c)||(a=a||"server"),a},l.on("error",function(a,d){b.retried=b.retried||0,b.chunks>1&&~"http,abort".indexOf(a)&&b.retried1&&f.extend(m,{chunks:b.chunks,chunk:b.chunk}),i.trigger("uploadBeforeSend",b,m,n),l.appendBlob(j.fileVal,b.blob,k.name),l.append(m),l.setRequestHeader(n),l.send()},_finishFile:function(a,b,c){var d=this.owner;return d.request("after-send-file",arguments,function(){a.setStatus(h.COMPLETE),d.trigger("uploadSuccess",a,b,c)}).fail(function(b){a.getStatus()===h.PROGRESS&&a.setStatus(h.ERROR,b),d.trigger("uploadError",a,b)}).always(function(){d.trigger("uploadComplete",a)})}})}),b("widgets/validator",["base","uploader","file","widgets/widget"],function(a,b,c){var d,e=a.$,f={};return d={addValidator:function(a,b){f[a]=b},removeValidator:function(a){delete f[a]}},b.register({name:"validator",init:function(){var b=this;a.nextTick(function(){e.each(f,function(){this.call(b.owner)})})}}),d.addValidator("fileNumLimit",function(){var a=this,b=a.options,c=0,d=parseInt(b.fileNumLimit,10),e=!0;d&&(a.on("beforeFileQueued",function(a){return c>=d&&e&&(e=!1,this.trigger("error","Q_EXCEED_NUM_LIMIT",d,a),setTimeout(function(){e=!0},1)),c>=d?!1:!0}),a.on("fileQueued",function(){c++}),a.on("fileDequeued",function(){c--}),a.on("reset",function(){c=0}))}),d.addValidator("fileSizeLimit",function(){var a=this,b=a.options,c=0,d=parseInt(b.fileSizeLimit,10),e=!0;d&&(a.on("beforeFileQueued",function(a){var b=c+a.size>d;return b&&e&&(e=!1,this.trigger("error","Q_EXCEED_SIZE_LIMIT",d,a),setTimeout(function(){e=!0},1)),b?!1:!0}),a.on("fileQueued",function(a){c+=a.size}),a.on("fileDequeued",function(a){c-=a.size}),a.on("reset",function(){c=0}))}),d.addValidator("fileSingleSizeLimit",function(){var a=this,b=a.options,d=b.fileSingleSizeLimit;d&&a.on("beforeFileQueued",function(a){return a.size>d?(a.setStatus(c.Status.INVALID,"exceed_size"),this.trigger("error","F_EXCEED_SIZE",d,a),!1):void 0})}),d.addValidator("duplicate",function(){function a(a){for(var b,c=0,d=0,e=a.length;e>d;d++)b=a.charCodeAt(d),c=b+(c<<6)+(c<<16)-c;return c}var b=this,c=b.options,d={};c.duplicate||(b.on("beforeFileQueued",function(b){var c=b.__hash||(b.__hash=a(b.name+b.size+b.lastModifiedDate));return d[c]?(this.trigger("error","F_DUPLICATE",b),!1):void 0}),b.on("fileQueued",function(a){var b=a.__hash;b&&(d[b]=!0)}),b.on("fileDequeued",function(a){var b=a.__hash;b&&delete d[b]}),b.on("reset",function(){d={}}))}),d}),b("runtime/compbase",[],function(){function a(a,b){this.owner=a,this.options=a.options,this.getRuntime=function(){return b},this.getRuid=function(){return b.uid},this.trigger=function(){return a.trigger.apply(a,arguments)}}return a}),b("runtime/html5/runtime",["base","runtime/runtime","runtime/compbase"],function(b,c,d){function e(){var a={},d=this,e=this.destroy;c.apply(d,arguments),d.type=f,d.exec=function(c,e){var f,h=this,i=h.uid,j=b.slice(arguments,2);return g[c]&&(f=a[i]=a[i]||new g[c](h,d),f[e])?f[e].apply(f,j):void 0},d.destroy=function(){return e&&e.apply(this,arguments)}}var f="html5",g={};return b.inherits(c,{constructor:e,init:function(){var a=this;setTimeout(function(){a.trigger("ready")},1)}}),e.register=function(a,c){var e=g[a]=b.inherits(d,c);return e},a.Blob&&a.FileReader&&a.DataView&&c.addRuntime(f,e),e}),b("runtime/html5/blob",["runtime/html5/runtime","lib/blob"],function(a,b){return a.register("Blob",{slice:function(a,c){var d=this.owner.source,e=d.slice||d.webkitSlice||d.mozSlice; 2 | return d=e.call(d,a,c),new b(this.getRuid(),d)}})}),b("runtime/html5/dnd",["base","runtime/html5/runtime","lib/file"],function(a,b,c){var d=a.$,e="webuploader-dnd-";return b.register("DragAndDrop",{init:function(){var b=this.elem=this.options.container;this.dragEnterHandler=a.bindFn(this._dragEnterHandler,this),this.dragOverHandler=a.bindFn(this._dragOverHandler,this),this.dragLeaveHandler=a.bindFn(this._dragLeaveHandler,this),this.dropHandler=a.bindFn(this._dropHandler,this),this.dndOver=!1,b.on("dragenter",this.dragEnterHandler),b.on("dragover",this.dragOverHandler),b.on("dragleave",this.dragLeaveHandler),b.on("drop",this.dropHandler),this.options.disableGlobalDnd&&(d(document).on("dragover",this.dragOverHandler),d(document).on("drop",this.dropHandler))},_dragEnterHandler:function(a){var b,c=this,d=c._denied||!1;return a=a.originalEvent||a,c.dndOver||(c.dndOver=!0,b=a.dataTransfer.items,b&&b.length&&(c._denied=d=!c.trigger("accept",b)),c.elem.addClass(e+"over"),c.elem[d?"addClass":"removeClass"](e+"denied")),a.dataTransfer.dropEffect=d?"none":"copy",!1},_dragOverHandler:function(a){var b=this.elem.parent().get(0);return b&&!d.contains(b,a.currentTarget)?!1:(clearTimeout(this._leaveTimer),this._dragEnterHandler.call(this,a),!1)},_dragLeaveHandler:function(){var a,b=this;return a=function(){b.dndOver=!1,b.elem.removeClass(e+"over "+e+"denied")},clearTimeout(b._leaveTimer),b._leaveTimer=setTimeout(a,100),!1},_dropHandler:function(a){var b,f,g=this,h=g.getRuid(),i=g.elem.parent().get(0);if(i&&!d.contains(i,a.currentTarget))return!1;a=a.originalEvent||a,b=a.dataTransfer;try{f=b.getData("text/html")}catch(j){}return f?void 0:(g._getTansferFiles(b,function(a){g.trigger("drop",d.map(a,function(a){return new c(h,a)}))}),g.dndOver=!1,g.elem.removeClass(e+"over"),!1)},_getTansferFiles:function(b,c){var d,e,f,g,h,i,j,k=[],l=[];for(d=b.items,e=b.files,j=!(!d||!d[0].webkitGetAsEntry),h=0,i=e.length;i>h;h++)f=e[h],g=d&&d[h],j&&g.webkitGetAsEntry().isDirectory?l.push(this._traverseDirectoryTree(g.webkitGetAsEntry(),k)):k.push(f);a.when.apply(a,l).done(function(){k.length&&c(k)})},_traverseDirectoryTree:function(b,c){var d=a.Deferred(),e=this;return b.isFile?b.file(function(a){c.push(a),d.resolve()}):b.isDirectory&&b.createReader().readEntries(function(b){var f,g=b.length,h=[],i=[];for(f=0;g>f;f++)h.push(e._traverseDirectoryTree(b[f],i));a.when.apply(a,h).then(function(){c.push.apply(c,i),d.resolve()},d.reject)}),d.promise()},destroy:function(){var a=this.elem;a&&(a.off("dragenter",this.dragEnterHandler),a.off("dragover",this.dragOverHandler),a.off("dragleave",this.dragLeaveHandler),a.off("drop",this.dropHandler),this.options.disableGlobalDnd&&(d(document).off("dragover",this.dragOverHandler),d(document).off("drop",this.dropHandler)))}})}),b("runtime/html5/filepaste",["base","runtime/html5/runtime","lib/file"],function(a,b,c){return b.register("FilePaste",{init:function(){var b,c,d,e,f=this.options,g=this.elem=f.container,h=".*";if(f.accept){for(b=[],c=0,d=f.accept.length;d>c;c++)e=f.accept[c].mimeTypes,e&&b.push(e);b.length&&(h=b.join(","),h=h.replace(/,/g,"|").replace(/\*/g,".*"))}this.accept=h=new RegExp(h,"i"),this.hander=a.bindFn(this._pasteHander,this),g.on("paste",this.hander)},_pasteHander:function(a){var b,d,e,f,g,h=[],i=this.getRuid();for(a=a.originalEvent||a,b=a.clipboardData.items,f=0,g=b.length;g>f;f++)d=b[f],"file"===d.kind&&(e=d.getAsFile())&&h.push(new c(i,e));h.length&&(a.preventDefault(),a.stopPropagation(),this.trigger("paste",h))},destroy:function(){this.elem.off("paste",this.hander)}})}),b("runtime/html5/filepicker",["base","runtime/html5/runtime"],function(a,b){var c=a.$;return b.register("FilePicker",{init:function(){var a,b,d,e,f=this.getRuntime().getContainer(),g=this,h=g.owner,i=g.options,j=this.label=c(document.createElement("label")),k=this.input=c(document.createElement("input"));if(k.attr("type","file"),k.attr("name",i.name),k.addClass("webuploader-element-invisible"),j.on("click",function(){k.trigger("click")}),j.css({opacity:0,width:"100%",height:"100%",display:"block",cursor:"pointer",background:"#ffffff"}),i.multiple&&k.attr("multiple","multiple"),i.accept&&i.accept.length>0){for(a=[],b=0,d=i.accept.length;d>b;b++)a.push(i.accept[b].mimeTypes);k.attr("accept",a.join(","))}f.append(k),f.append(j),e=function(a){h.trigger(a.type)},k.on("change",function(a){var b,d=arguments.callee;g.files=a.target.files,b=this.cloneNode(!0),b.value=null,this.parentNode.replaceChild(b,this),k.off(),k=c(b).on("change",d).on("mouseenter mouseleave",e),h.trigger("change")}),j.on("mouseenter mouseleave",e)},getFiles:function(){return this.files},destroy:function(){this.input.off(),this.label.off()}})}),b("runtime/html5/transport",["base","runtime/html5/runtime"],function(a,b){var c=a.noop,d=a.$;return b.register("Transport",{init:function(){this._status=0,this._response=null},send:function(){var b,c,e,f=this.owner,g=this.options,h=this._initAjax(),i=f._blob,j=g.server;g.sendAsBinary?(j+=(/\?/.test(j)?"&":"?")+d.param(f._formData),c=i.getSource()):(b=new FormData,d.each(f._formData,function(a,c){b.append(a,c)}),b.append(g.fileVal,i.getSource(),g.filename||f._formData.name||"")),g.withCredentials&&"withCredentials"in h?(h.open(g.method,j,!0),h.withCredentials=!0):h.open(g.method,j),this._setRequestHeader(h,g.headers),c?(h.overrideMimeType&&h.overrideMimeType("application/octet-stream"),a.os.android?(e=new FileReader,e.onload=function(){h.send(this.result),e=e.onload=null},e.readAsArrayBuffer(c)):h.send(c)):h.send(b)},getResponse:function(){return this._response},getResponseAsJson:function(){return this._parseJson(this._response)},getStatus:function(){return this._status},abort:function(){var a=this._xhr;a&&(a.upload.onprogress=c,a.onreadystatechange=c,a.abort(),this._xhr=a=null)},destroy:function(){this.abort()},_initAjax:function(){var a=this,b=new XMLHttpRequest,d=this.options;return!d.withCredentials||"withCredentials"in b||"undefined"==typeof XDomainRequest||(b=new XDomainRequest),b.upload.onprogress=function(b){var c=0;return b.lengthComputable&&(c=b.loaded/b.total),a.trigger("progress",c)},b.onreadystatechange=function(){return 4===b.readyState?(b.upload.onprogress=c,b.onreadystatechange=c,a._xhr=null,a._status=b.status,b.status>=200&&b.status<300?(a._response=b.responseText,a.trigger("load")):b.status>=500&&b.status<600?(a._response=b.responseText,a.trigger("error","server")):a.trigger("error",a._status?"http":"abort")):void 0},a._xhr=b,b},_setRequestHeader:function(a,b){d.each(b,function(b,c){a.setRequestHeader(b,c)})},_parseJson:function(a){var b;try{b=JSON.parse(a)}catch(c){b={}}return b}})}),b("runtime/flash/runtime",["base","runtime/runtime","runtime/compbase"],function(b,c,d){function e(){var a;try{a=navigator.plugins["Shockwave Flash"],a=a.description}catch(b){try{a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version")}catch(c){a="0.0"}}return a=a.match(/\d+/g),parseFloat(a[0]+"."+a[1],10)}function f(){function d(a,b){var c,d,e=a.type||a;c=e.split("::"),d=c[0],e=c[1],"Ready"===e&&d===j.uid?j.trigger("ready"):f[d]&&f[d].trigger(e.toLowerCase(),a,b)}var e={},f={},g=this.destroy,j=this,k=b.guid("webuploader_");c.apply(j,arguments),j.type=h,j.exec=function(a,c){var d,g=this,h=g.uid,k=b.slice(arguments,2);return f[h]=g,i[a]&&(e[h]||(e[h]=new i[a](g,j)),d=e[h],d[c])?d[c].apply(d,k):j.flashExec.apply(g,arguments)},a[k]=function(){var a=arguments;setTimeout(function(){d.apply(null,a)},1)},this.jsreciver=k,this.destroy=function(){return g&&g.apply(this,arguments)},this.flashExec=function(a,c){var d=j.getFlash(),e=b.slice(arguments,2);return d.exec(this.uid,a,c,e)}}var g=b.$,h="flash",i={};return b.inherits(c,{constructor:f,init:function(){var a,c=this.getContainer(),d=this.options;c.css({position:"absolute",top:"-8px",left:"-8px",width:"9px",height:"9px",overflow:"hidden"}),a='',c.html(a)},getFlash:function(){return this._flash?this._flash:(this._flash=g("#"+this.uid).get(0),this._flash)}}),f.register=function(a,c){return c=i[a]=b.inherits(d,g.extend({flashExec:function(){var a=this.owner,b=this.getRuntime();return b.flashExec.apply(a,arguments)}},c))},e()>=11.4&&c.addRuntime(h,f),f}),b("runtime/flash/filepicker",["base","runtime/flash/runtime"],function(a,b){var c=a.$;return b.register("FilePicker",{init:function(a){var b,d,e=c.extend({},a);for(b=e.accept&&e.accept.length,d=0;b>d;d++)e.accept[d].title||(e.accept[d].title="Files");delete e.button,delete e.id,delete e.container,this.flashExec("FilePicker","init",e)},destroy:function(){this.flashExec("FilePicker","destroy")}})}),b("runtime/flash/transport",["base","runtime/flash/runtime","runtime/client"],function(b,c,d){var e=b.$;return c.register("Transport",{init:function(){this._status=0,this._response=null,this._responseJson=null},send:function(){var a,b=this.owner,c=this.options,d=this._initAjax(),f=b._blob,g=c.server;d.connectRuntime(f.ruid),c.sendAsBinary?(g+=(/\?/.test(g)?"&":"?")+e.param(b._formData),a=f.uid):(e.each(b._formData,function(a,b){d.exec("append",a,b)}),d.exec("appendBlob",c.fileVal,f.uid,c.filename||b._formData.name||"")),this._setRequestHeader(d,c.headers),d.exec("send",{method:c.method,url:g,forceURLStream:c.forceURLStream,mimeType:"application/octet-stream"},a)},getStatus:function(){return this._status},getResponse:function(){return this._response||""},getResponseAsJson:function(){return this._responseJson},abort:function(){var a=this._xhr;a&&(a.exec("abort"),a.destroy(),this._xhr=a=null)},destroy:function(){this.abort()},_initAjax:function(){var b=this,c=new d("XMLHttpRequest");return c.on("uploadprogress progress",function(a){var c=a.loaded/a.total;return c=Math.min(1,Math.max(0,c)),b.trigger("progress",c)}),c.on("load",function(){var d,e=c.exec("getStatus"),f=!1,g="";return c.off(),b._xhr=null,e>=200&&300>e?f=!0:e>=500&&600>e?(f=!0,g="server"):g="http",f&&(b._response=c.exec("getResponse"),b._response=decodeURIComponent(b._response),d=a.JSON&&a.JSON.parse||function(a){try{return new Function("return "+a).call()}catch(b){return{}}},b._responseJson=b._response?d(b._response):{}),c.destroy(),c=null,g?b.trigger("error",g):b.trigger("load")}),c.on("error",function(){c.off(),b._xhr=null,b.trigger("error","http")}),b._xhr=c,c},_setRequestHeader:function(a,b){e.each(b,function(b,c){a.exec("setRequestHeader",b,c)})}})}),b("preset/withoutimage",["base","widgets/filednd","widgets/filepaste","widgets/filepicker","widgets/queue","widgets/runtime","widgets/upload","widgets/validator","runtime/html5/blob","runtime/html5/dnd","runtime/html5/filepaste","runtime/html5/filepicker","runtime/html5/transport","runtime/flash/filepicker","runtime/flash/transport"],function(a){return a}),b("webuploader",["preset/withoutimage"],function(a){return a}),c("webuploader")}); --------------------------------------------------------------------------------