├── .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 |
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")});
--------------------------------------------------------------------------------