upload.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. (function($) {
  2. // 当domReady的时候开始初始化
  3. $(function() {
  4. var $wrap = $('#uploader'),
  5. // 图片容器
  6. $queue = $('<ul class="filelist"></ul>')
  7. .appendTo($wrap.find('.queueList')),
  8. // 状态栏,包括进度和控制按钮
  9. $statusBar = $wrap.find('.statusBar'),
  10. // 文件总体选择信息。
  11. $info = $statusBar.find('.info'),
  12. // 上传按钮
  13. $upload = $wrap.find('.uploadBtn'),
  14. // 没选择文件之前的内容。
  15. $placeHolder = $wrap.find('.placeholder'),
  16. $progress = $statusBar.find('.progress').hide(),
  17. // 添加的文件数量
  18. fileCount = 0,
  19. // 添加的文件总大小
  20. fileSize = 0,
  21. // 可能有pedding, ready, uploading, confirm, done.
  22. state = 'pedding',
  23. // 所有文件的进度信息,key为file id
  24. percentages = {},
  25. // WebUploader实例
  26. uploader;
  27. // 实例化
  28. uploader = WebUploader.create({
  29. pick: '#filePicker',
  30. dnd: '#dndArea',
  31. chunked: true,
  32. accept: '',
  33. server: './server/fileupload.php',
  34. // server: 'http://www.2betop.net/fileupload.php',
  35. });
  36. // 添加“添加文件”的按钮,
  37. uploader.addButton({
  38. id: '#filePicker2',
  39. label: '继续添加'
  40. });
  41. // 当有文件添加进来时执行,负责view的创建
  42. function addFile(file) {
  43. var $li = $('<li id="' + file.id + '">' +
  44. '<p class="title">' + file.name + '</p>' +
  45. '<p class="imgWrap"></p>' +
  46. '<p class="progress"><span></span></p>' +
  47. '</li>'),
  48. $btns = $('<div class="file-panel">' +
  49. '<span class="cancel">删除</span>' + '</div>').appendTo($li),
  50. $prgress = $li.find('p.progress span'),
  51. $wrap = $li.find('p.imgWrap'),
  52. $info = $('<p class="error"></p>'),
  53. showError = function(code) {
  54. switch (code) {
  55. case 'interrupt':
  56. text = '上传暂停';
  57. break;
  58. default:
  59. text = '上传失败,请重试';
  60. break;
  61. }
  62. $info.text(text).appendTo($li);
  63. };
  64. if (file.getStatus() === 'invalid') {
  65. showError(file.statusText);
  66. } else {
  67. $wrap.text('不能预览');
  68. percentages[file.id] = [file.size, 0];
  69. file.rotation = 0;
  70. }
  71. file.on('statuschange', function(cur, prev) {
  72. if (prev === 'progress') {
  73. $prgress.hide().width(0);
  74. } else if (prev === 'queued') {
  75. $li.off('mouseenter mouseleave');
  76. $btns.remove();
  77. }
  78. // 成功
  79. if (cur === 'error' || cur === 'invalid') {
  80. showError(file.statusText);
  81. percentages[file.id][1] = 1;
  82. } else if (cur === 'interrupt') {
  83. showError('interrupt');
  84. } else if (cur === 'queued') {
  85. percentages[file.id][1] = 0;
  86. } else if (cur === 'progress') {
  87. $info.remove();
  88. $prgress.css({
  89. display: 'block',
  90. width: '10%'
  91. });
  92. } else if (cur === 'complete') {
  93. $li.append('<span class="success"></span>');
  94. }
  95. $li.removeClass('state-' + prev).addClass('state-' + cur);
  96. });
  97. $li.on('mouseenter', function() {
  98. $btns.stop().animate({
  99. height: 30
  100. });
  101. });
  102. $li.on('mouseleave', function() {
  103. $btns.stop().animate({
  104. height: 0
  105. });
  106. });
  107. $btns.on('click', 'span', function() {
  108. uploader.removeFile(file);
  109. });
  110. $li.appendTo($queue);
  111. }
  112. // 负责view的销毁
  113. function removeFile(file) {
  114. var $li = $('#' + file.id);
  115. delete percentages[file.id];
  116. updateTotalProgress();
  117. $li.off().find('.file-panel').off().end().remove();
  118. }
  119. function updateTotalProgress() {
  120. var loaded = 0,
  121. total = 0,
  122. spans = $progress.children(),
  123. percent;
  124. $.each(percentages, function(k, v) {
  125. total += v[0];
  126. loaded += v[0] * v[1];
  127. });
  128. percent = total ? loaded / total : 0;
  129. spans.eq(0).text(Math.round(percent * 100) + '%');
  130. spans.eq(1).css('width', Math.round(percent * 100) + '%');
  131. updateStatus();
  132. }
  133. function updateStatus() {
  134. var text = '',
  135. stats;
  136. if (state === 'ready') {
  137. text = '选中' + fileCount + '张图片,共' +
  138. WebUploader.formatSize(fileSize) + '。';
  139. } else if (state === 'confirm') {
  140. stats = uploader.getStats();
  141. if (stats.uploadFailNum) {
  142. text = '已成功上传' + stats.successNum + '张照片至XX相册,' +
  143. stats.uploadFailNum + '张照片上传失败,<a class="retry" href="#">重新上传</a>失败图片或<a class="ignore" href="#">忽略</a>'
  144. }
  145. } else {
  146. stats = uploader.getStats();
  147. text = '共' + fileCount + '张(' +
  148. WebUploader.formatSize(fileSize) +
  149. '),已上传' + stats.successNum + '张';
  150. if (stats.uploadFailNum) {
  151. text += ',失败' + stats.uploadFailNum + '张';
  152. }
  153. }
  154. $info.html(text);
  155. }
  156. function setState(val) {
  157. var file, stats;
  158. if (val === state) {
  159. return;
  160. }
  161. $upload.removeClass('state-' + state);
  162. $upload.addClass('state-' + val);
  163. state = val;
  164. switch (state) {
  165. case 'pedding':
  166. $placeHolder.removeClass('element-invisible');
  167. $queue.hide();
  168. $statusBar.addClass('element-invisible');
  169. uploader.refresh();
  170. break;
  171. case 'ready':
  172. $placeHolder.addClass('element-invisible');
  173. $('#filePicker2').removeClass('element-invisible');
  174. $queue.show();
  175. $statusBar.removeClass('element-invisible');
  176. uploader.refresh();
  177. break;
  178. case 'uploading':
  179. $('#filePicker2').addClass('element-invisible');
  180. $progress.show();
  181. $upload.text('暂停上传');
  182. break;
  183. case 'paused':
  184. $progress.show();
  185. $upload.text('继续上传');
  186. break;
  187. case 'confirm':
  188. $progress.hide();
  189. $upload.text('开始上传').addClass('disabled');
  190. stats = uploader.getStats();
  191. if (stats.successNum && !stats.uploadFailNum) {
  192. setState('finish');
  193. return;
  194. }
  195. break;
  196. case 'finish':
  197. stats = uploader.getStats();
  198. if (stats.successNum) {
  199. alert('上传成功');
  200. } else {
  201. // 没有成功的图片,重设
  202. state = 'done';
  203. location.reload();
  204. }
  205. break;
  206. }
  207. updateStatus();
  208. }
  209. uploader.onUploadProgress = function(file, percentage) {
  210. var $li = $('#' + file.id),
  211. $percent = $li.find('.progress span');
  212. $percent.css('width', percentage * 100 + '%');
  213. percentages[file.id][1] = percentage;
  214. updateTotalProgress();
  215. };
  216. uploader.onFileQueued = function(file) {
  217. fileCount++;
  218. fileSize += file.size;
  219. if (fileCount === 1) {
  220. $placeHolder.addClass('element-invisible');
  221. $statusBar.show();
  222. }
  223. addFile(file);
  224. setState('ready');
  225. updateTotalProgress();
  226. };
  227. uploader.onFileDequeued = function(file) {
  228. fileCount--;
  229. fileSize -= file.size;
  230. if (!fileCount) {
  231. setState('pedding');
  232. }
  233. removeFile(file);
  234. updateTotalProgress();
  235. };
  236. uploader.on('all', function(type) {
  237. var stats;
  238. switch (type) {
  239. case 'uploadFinished':
  240. setState('confirm');
  241. break;
  242. case 'startUpload':
  243. setState('uploading');
  244. break;
  245. case 'stopUpload':
  246. setState('paused');
  247. break;
  248. }
  249. });
  250. uploader.onError = function(code) {
  251. alert('Eroor: ' + code);
  252. };
  253. $upload.on('click', function() {
  254. if ($(this).hasClass('disabled')) {
  255. return false;
  256. }
  257. if (state === 'ready') {
  258. uploader.upload();
  259. } else if (state === 'paused') {
  260. uploader.upload();
  261. } else if (state === 'uploading') {
  262. uploader.stop();
  263. }
  264. });
  265. $info.on('click', '.retry', function() {
  266. uploader.retry();
  267. });
  268. $info.on('click', '.ignore', function() {
  269. alert('todo');
  270. });
  271. $upload.addClass('state-' + state);
  272. updateTotalProgress();
  273. });
  274. })(jQuery);
  275. (function($) {
  276. // 扩展md5逻辑
  277. var Uploader = WebUploader.Uploader;
  278. var fr;
  279. function readContent(file, cb) {
  280. var chunkSize = 2 * 1024 * 1024,
  281. chunks = Math.ceil(file.size / chunkSize),
  282. chunk = 0,
  283. ret = '',
  284. blobSlice = file.mozSlice || file.webkitSlice || file.slice,
  285. loadNext;
  286. fr = fr || new FileReader;
  287. loadNext = function() {
  288. var start, end;
  289. start = chunk * chunkSize;
  290. end = start + chunkSize >= file.size ? file.size : start + chunkSize;
  291. fr.onload = function(e) {
  292. ret += fr.result;
  293. fr.result = null;
  294. };
  295. fr.onloadend = function() {
  296. fr.onload = fr.onloadend = null;
  297. if (++chunk < chunks) {
  298. setTimeout(loadNext, 1);
  299. } else {
  300. fr.readAsBinaryString(new Blob(['a'], {
  301. type: 'text/plain'
  302. }));
  303. cb(ret);
  304. ret = loadNext = blobSlice = file = null;
  305. }
  306. };
  307. fr.readAsBinaryString(blobSlice.call(file, start, end));
  308. };
  309. loadNext();
  310. }
  311. function Md5File(file, callback) {
  312. console.time('get md5: ' + file.name);
  313. readContent(file, function(ret) {
  314. ret = md5(ret);
  315. console.timeEnd('get md5: ' + file.name);
  316. setTimeout(function() {
  317. callback(ret);
  318. }, 1);
  319. });
  320. }
  321. Uploader.register({
  322. 'before-send-file': 'preupload'
  323. }, {
  324. preupload: function(file) {
  325. var me = this,
  326. owner = this.owner,
  327. server = me.options.server,
  328. deferred = WebUploader.Deferred(),
  329. blob = file.source.getSource();
  330. Md5File(blob, function(ret) {
  331. $.ajax(server, {
  332. dataType: 'json',
  333. data: {
  334. md5: ret
  335. },
  336. success: function( response ) {
  337. if ( response.exist ) {
  338. owner.skipFile( file );
  339. var log = $('#'+file.id).find('p.imgWrap')
  340. log.text('文件重复,已跳过');
  341. }
  342. deferred.resolve( true );
  343. }
  344. });
  345. });
  346. return deferred.promise();
  347. }
  348. });
  349. })(jQuery);