html5.websocket.fileupload-2.0.0.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. /**
  2. * modify 2017/9/25.
  3. */
  4. (function($) {
  5. var FileUploader = function(element, options) {
  6. var that = this;
  7. this.element = $(element);
  8. this.container = options.container || "body";
  9. this.isInput = options.isInput || true;
  10. this.isInline = true;
  11. this.concurrentHash = options.concurrentHash || 3;
  12. this.concurrentUpload = options.concurrentUpload || 3;
  13. this.concurrentHashCurrent = 0;
  14. this.wsuri = options.wsuri;
  15. this.debugMode = options.debugMode || false;
  16. this.onSuccess = options.onSuccess;
  17. this.fileInput = $(UPGlobal.fileInput)
  18. .appendTo(this.container)
  19. .on({
  20. change: $.proxy(this.fileSelect, this)
  21. });
  22. if (this.debugMode) {
  23. UPGlobal.template += UPGlobal.consTemplate;
  24. }
  25. this.panel = $(UPGlobal.template)
  26. .appendTo(this.isInline ? this.element : this.container)
  27. .on({
  28. click: $.proxy(this.click, this)
  29. });
  30. this.attachDragEvents();
  31. this.initWS();
  32. };
  33. FileUploader.prototype = {
  34. initWS: function() {
  35. if ("WebSocket" in window) {
  36. for (var i = 0; i < this.concurrentUpload; i++) {
  37. var ws = new WebSocket(this.wsuri);
  38. ws.onopen = this.onOpen(this);
  39. ws.onmessage = this.onMessage(this);
  40. UPGlobal.wsS.push(ws);
  41. }
  42. } else if ("MozWebSocket" in window) {
  43. for (var i = 0; i < this.concurrentUpload; i++) {
  44. var ws = new MozWebSocket(this.wsuri);
  45. ws.onopen = this.onOpen;
  46. ws.onmessage = this.onMessage;
  47. UPGlobal.wsS.push(ws);
  48. }
  49. } else {
  50. alert("Websocket create error.");
  51. }
  52. },
  53. onOpen: function(that) {
  54. return function() {
  55. that.log("Websocket is opened.");
  56. };
  57. },
  58. onMessage: function(that) {
  59. return function(e) {
  60. var ws = this;
  61. var obj = JSON.parse(e.data);
  62. if (obj.typeId == "uploadCommand") {
  63. that.log(
  64. "文件完成:" + parseInt(obj.completePercent * 65 + 35, 10) + "%"
  65. );
  66. $('[name="' + obj.fileId + '"]').css(
  67. "width",
  68. parseInt(obj.completePercent * 65 + 35, 10) + "%"
  69. );
  70. if (obj.completePercent != 1) {
  71. that.doUploadByCommand(ws, obj);
  72. } else {
  73. UPGlobal.fileUploadStepMap[
  74. UPGlobal.fileIdToFileNameMap[obj.fileId]
  75. ] = 4; //onSuccess
  76. if (that.onSuccess) {
  77. //console.log(obj);
  78. that.onSuccess({
  79. fileId: obj.fileId,
  80. fileName: obj.fileName,
  81. fileSize: obj.fileSize
  82. });
  83. }
  84. that.log("文件上传完成,开始上传下一个文件。");
  85. that.uploadNextFile(ws); //该ws完成文件上传,接着继续上传下一个文件
  86. $('[name="' + obj.fileId + '"]')
  87. .closest(".progress")
  88. .slideUp(1000)
  89. .closest("td")
  90. .css("color", "#428bca");
  91. }
  92. } //if end
  93. };
  94. },
  95. hashNextFile: function() {
  96. if (UPGlobal.filesToUpload.length != 0) {
  97. var f = UPGlobal.filesToUpload.shift();
  98. var worker = new Worker("fileupload/calculator.worker.fileId.js");
  99. worker.addEventListener("message", this.handleHashWorkerEvent(f));
  100. UPGlobal.workers[f.name] = worker; //记录每个文件的worker用于后面清除worker
  101. this.doHashWork(f, worker);
  102. } else {
  103. //Hash计算完毕要恢复此值
  104. this.concurrentHashCurrent = 0;
  105. }
  106. },
  107. startHashFile: function() {
  108. for (
  109. var f, worker;
  110. this.concurrentHashCurrent < this.concurrentHash;
  111. this.concurrentHashCurrent++
  112. ) {
  113. if (UPGlobal.filesToUpload.length == 0) break;
  114. f = UPGlobal.filesToUpload.shift();
  115. worker = new Worker(
  116. "../../AFrontEnd/plugins/fileupload/js/calculator.worker.fileId.js"
  117. );
  118. worker.addEventListener("message", this.handleHashWorkerEvent(f));
  119. UPGlobal.workers[f.name] = worker; //记录每个文件的worker用于后面清除worker
  120. this.doHashWork(f, worker);
  121. }
  122. },
  123. doHashWork: function(file, worker) {
  124. UPGlobal.fileUploadStepMap[file.name] = 1; //文件上传进入计算文件hash状态
  125. var i,
  126. buffer_size,
  127. block,
  128. reader,
  129. blob,
  130. handle_hash_block,
  131. handle_load_block;
  132. //发送数据
  133. handle_load_block = function(event) {
  134. worker.postMessage({
  135. message: event.target.result,
  136. block: block
  137. });
  138. };
  139. handle_hash_block = function(event) {
  140. if (block.end !== file.size) {
  141. block.start += buffer_size;
  142. block.end += buffer_size;
  143. if (block.end > file.size) {
  144. block.end = file.size;
  145. }
  146. reader = new FileReader();
  147. reader.onload = handle_load_block;
  148. blob = file.slice(block.start, block.end);
  149. reader.readAsArrayBuffer(blob);
  150. }
  151. };
  152. buffer_size = 512 * 1024;
  153. block = {
  154. file_size: file.size,
  155. start: 0
  156. };
  157. block.end = buffer_size > file.size ? file.size : buffer_size;
  158. worker.addEventListener("message", handle_hash_block);
  159. reader = new FileReader();
  160. reader.onload = handle_load_block;
  161. blob = file.slice(block.start, block.end);
  162. reader.readAsArrayBuffer(blob);
  163. },
  164. startUploadFile: function(f) {
  165. if (UPGlobal.wsS.length != 0) {
  166. var ws = UPGlobal.wsS.shift(); //返回第一个元素,pop()返回最后一个元素。
  167. this.sendFileInfo(ws, f);
  168. this.log(
  169. "已从ws连接池中取出第一个ws连接,已发送[" +
  170. f.name +
  171. "]文件信息,正在使用的ws连接加1."
  172. );
  173. UPGlobal.filesUploaded.push(f);
  174. } else {
  175. UPGlobal.filesHasHashed.push(f);
  176. UPGlobal.fileUploadStepMap[f.name] = 2; //onWaiting
  177. this.log(
  178. "ws连接池中没有可用连接,已将[" + f.name + "]放入已计算Hash文件池中."
  179. );
  180. }
  181. },
  182. uploadNextFile: function(ws) {
  183. if (UPGlobal.filesHasHashed.length != 0) {
  184. var f = UPGlobal.filesHasHashed.shift();
  185. this.sendFileInfo(ws, f);
  186. this.log(
  187. "从已计算Hash文件池中取出文件[" + f.name + "],并发送文件信息."
  188. );
  189. UPGlobal.filesUploaded.push(f);
  190. } else {
  191. //文件上传结束,回收ws连接
  192. UPGlobal.wsS.push(ws);
  193. this.log("没有已计算出Hash值的文件可上传,回收ws连接到连接池中.");
  194. }
  195. },
  196. sendFileInfo: function(ws, f) {
  197. var fileInfo = {};
  198. fileInfo.fileName = f.name;
  199. fileInfo.fileSize = f.size;
  200. fileInfo.fileInfo = f.type;
  201. fileInfo.fileId = f.fileId;
  202. ws.send(JSON.stringify(fileInfo));
  203. UPGlobal.fileUploadStepMap[f.name] = 3; //onUploading
  204. },
  205. doUploadByCommand: function(ws, command) {
  206. var fileName = UPGlobal.fileIdToFileNameMap[command.fileId];
  207. var f = UPGlobal.allFiles[fileName];
  208. if (f) {
  209. var blob = f.slice(command.indexStart, command.indexEnd);
  210. var reader = new FileReader();
  211. reader.onload = function(e) {
  212. // this.log('Sending data: '+blob.size);
  213. ws.send(e.target.result);
  214. };
  215. reader.readAsArrayBuffer(blob);
  216. } else {
  217. this.uploadNextFile(ws);
  218. }
  219. },
  220. handleHashWorkerEvent: function(f) {
  221. var that = this;
  222. return function(e) {
  223. if (e.data.result) {
  224. this.concurrentHashCurrent--;
  225. var fileId = e.data.result;
  226. console.log(fileId);
  227. //此处设置到name属性而不是id属性,是因为如果本地有相同fileId时,则进度显示会出问题(不会影响文件上传)。
  228. $('[name="' + f.name + '"]')
  229. .find(".progress-bar")
  230. .css("width", "35%")
  231. .attr("name", fileId);
  232. UPGlobal.workers[f.name].terminate(); //关闭workers
  233. delete UPGlobal.workers[f.name];
  234. f["fileId"] = fileId;
  235. UPGlobal.fileIdToFileNameMap[fileId] = f.name; //将文件对应的fileId存储到map中
  236. that.log("文件已算出hash值,开始上传文件:" + f.name);
  237. that.startUploadFile(f);
  238. that.hashNextFile(); //hash next file
  239. } else {
  240. $('[name="' + f.name + '"]')
  241. .find(".progress-bar")
  242. .css("width", e.data.block.end * 35 / e.data.block.file_size + "%");
  243. }
  244. };
  245. },
  246. click: function(e) {
  247. var target = $(e.target).closest("#addfile,td,.drop-zone");
  248. var addfile = this.panel.find("#addfile");
  249. var removefile = this.panel.find("tbody tr td:nth-last-child(1)");
  250. var dropZone = this.panel.find(".drop-zone");
  251. if (target.is(addfile) || target.is(dropZone)) {
  252. $("#files").val("");
  253. $("#files").trigger("click");
  254. } else if (target.is(removefile)) {
  255. var $this = target;
  256. var fileName = $this
  257. .closest("tr")
  258. .remove()
  259. .find(".progress-bar")
  260. .closest("td")
  261. .find("span")
  262. .html();
  263. //console.log(fileName);
  264. this.doRemoveFileWork(fileName);
  265. }
  266. },
  267. doRemoveFileWork: function(fileName) {
  268. var fileStep = UPGlobal.fileUploadStepMap[fileName];
  269. switch (fileStep) {
  270. case 0:
  271. //alert('nothing');
  272. //delete file from filesToUpload
  273. this.removeFileFromArray(fileName, UPGlobal.filesToUpload);
  274. break;
  275. case 1:
  276. //alert('onHashing');
  277. UPGlobal.workers[fileName].terminate();
  278. //delete file from filesHasHashed ??不需要,这个时候还没有放入filesHasHashed中
  279. this.concurrentHashCurrent--;
  280. this.removeFileFromArray(fileName);
  281. this.hashNextFile();
  282. break;
  283. case 2:
  284. //alert('onWaiting');
  285. //delete file from filesHasHashed
  286. this.removeFileFromArray(fileName, UPGlobal.filesHasHashed);
  287. break;
  288. case 3:
  289. //alert('onUploading');
  290. //delete file from filesUploaded ??不需要,这个时候还没有放入fileHasHashed中
  291. this.removeFileFromArray(fileName); //移除之后通过command上传将找不到file而终止文件上传。并自动开始上传下一个文件
  292. break;
  293. case 4:
  294. //alert('onSuccess');
  295. //delete file from filesUploaded
  296. this.removeFileFromArray(fileName, UPGlobal.filesUploaded);
  297. break;
  298. }
  299. },
  300. removeFileFromArray: function(fileName, filesArray) {
  301. delete UPGlobal.allFiles[fileName];
  302. console.log(UPGlobal.allFiles);
  303. if (filesArray) {
  304. for (var i = 0; i < filesArray.length; i++) {
  305. if (fileName === filesArray[i].name) {
  306. filesArray.splice(i, 1);
  307. break;
  308. }
  309. }
  310. }
  311. console.log(filesArray);
  312. //setAllHeigth(-5);
  313. },
  314. attachDragEvents: function() {
  315. var tablepanel = document.getElementsByClassName("tablepanel");
  316. tablepanel[0].addEventListener("dragover", $.proxy(this.dragOver, this));
  317. tablepanel[0].addEventListener("drop", $.proxy(this.fileSelect, this));
  318. },
  319. dragOver: function(e) {
  320. e.stopPropagation();
  321. e.preventDefault();
  322. },
  323. fileSelect: function(e) {
  324. e.stopPropagation();
  325. e.preventDefault();
  326. this.hideDropzone();
  327. var files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
  328. var output = [];
  329. for (var i = 0, f; (f = files[i]); i++) {
  330. if (!UPGlobal.allFiles[f.name]) {
  331. var process =
  332. '<div name="' +
  333. f.name +
  334. '" class="progress"><div class="progress-bar progress-bar-striped active" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div></div>';
  335. output.push(
  336. "<tr><td><span>" +
  337. f.name +
  338. "</span>(" +
  339. this.formatFileSize(f.size) +
  340. ")" +
  341. process +
  342. '</td><td><span class="glyphicon glyphicon-remove"></span></td></tr>'
  343. );
  344. UPGlobal.allFiles[f.name] = f;
  345. UPGlobal.filesToUpload.push(f);
  346. UPGlobal.fileUploadStepMap[f.name] = 0;
  347. }
  348. }
  349. this.panel.find("tbody").append(output.join(""));
  350. //setAllHeigth(-5);
  351. this.startHashFile(); //计算hash
  352. },
  353. formatFileSize: function(fileSize, lenght) {
  354. var r;
  355. var l = lenght || 2;
  356. for (var k in UPGlobal.fileSizeFormats) {
  357. var v = UPGlobal.fileSizeFormats[k];
  358. r = fileSize / v;
  359. if (r >= 1 && r < 1000) {
  360. r = parseInt(r * Math.pow(10, l), 10) / Math.pow(10, l) + k;
  361. break;
  362. }
  363. }
  364. return r;
  365. },
  366. hideDropzone: function() {
  367. $(".drop-zone").slideUp(500);
  368. },
  369. log: function(message) {
  370. if (this.debugMode) {
  371. var console = document.getElementById("console");
  372. var p = document.createElement("p");
  373. p.style.wordWrap = "break-word";
  374. p.appendChild(document.createTextNode(message));
  375. console.appendChild(p);
  376. while (console.childNodes.length > 50) {
  377. console.removeChild(console.firstChild);
  378. }
  379. console.scrollTop = console.scrollHeight;
  380. }
  381. }
  382. };
  383. $.fn.fileuploader = function(option) {
  384. var args = Array.apply(null, arguments);
  385. args.shift();
  386. var internal_return;
  387. this.each(function() {
  388. var $this = $(this),
  389. data = $this.data("fileuploader"),
  390. options = typeof option == "object" && option;
  391. if (!data) {
  392. $this.data(
  393. "fileuploader",
  394. (data = new FileUploader(
  395. this,
  396. $.extend({}, $.fn.fileuploader.defaults, options)
  397. ))
  398. );
  399. }
  400. if (typeof option == "string" && typeof data[option] == "function") {
  401. internal_return = data[option].apply(data, args);
  402. if (internal_return !== undefined) {
  403. return false;
  404. }
  405. }
  406. });
  407. if (internal_return != undefined) {
  408. return internal_return;
  409. } else {
  410. return this;
  411. }
  412. };
  413. $.fn.fileuploader.defaults = {};
  414. $.fn.fileuploader.Constructor = FileUploader;
  415. ///拼dom,简化版上传按钮控件
  416. var UPGlobal = {
  417. allFiles: {},
  418. filesToUpload: [],
  419. filesHasHashed: [],
  420. filesUploaded: [],
  421. fileIdToFileNameMap: {},
  422. fileUploadStepMap: {}, //0,nothing;1,onHashing;2,onWaiting;3,onUploading;4,onSuccess
  423. workers: {},
  424. wsS: [],
  425. fileSizeFormats: {
  426. Byte: Math.pow(10, 0),
  427. KB: Math.pow(10, 3),
  428. MB: Math.pow(10, 6),
  429. GB: Math.pow(10, 9),
  430. TB: Math.pow(10, 12),
  431. PB: Math.pow(10, 15),
  432. EB: Math.pow(10, 18),
  433. ZB: Math.pow(10, 21),
  434. YB: Math.pow(10, 24),
  435. BB: Math.pow(10, 27)
  436. },
  437. headTemplate: "",
  438. ///拼dom,简化版上传按钮控件
  439. contTemplate:
  440. '<div class="tablepanel panel-body" style="disP"><table class="table table-hover"><tbody></tbody></table>',
  441. footTemplate:
  442. '<div class="panel-footer"><div class="btn-group">' +
  443. '<div class="btn btn-default" id="addfile">添加</div>' +
  444. "</div></div>",
  445. consTemplate: '<div id="console" class="well"></div>',
  446. finpTemplate:
  447. '<input id="files" type="file" multiple style="display: none"/>'
  448. };
  449. UPGlobal.template =
  450. '<div class="panel panel-default">' +
  451. UPGlobal.headTemplate +
  452. UPGlobal.contTemplate +
  453. UPGlobal.footTemplate +
  454. "</div>";
  455. UPGlobal.fileInput = UPGlobal.finpTemplate;
  456. $.fn.fileuploader.UPGlobal = UPGlobal;
  457. })(jQuery);