upload.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. define(['md5'], function (SparkMD5, allowMime) {
  2. allowMime = JSON.parse('{$exts|raw}');
  3. return function (element, callable, option) {
  4. /*! 初始化变量 */
  5. option = {element: $(element), exts: [], mimes: [], files: {}, cache: {}, load: 0};
  6. option.count = {total: 0, uploaded: 0}, option.size = option.element.data('size') || 0;
  7. option.safe = option.element.data('safe') ? 1 : 0, option.hload = option.element.data('hide-load') ? 1 : 0;
  8. option.field = option.element.data('field') || 'file', option.input = $('[name="_field_"]'.replace('_field_', option.field));
  9. option.uptype = option.safe ? 'local' : option.element.attr('data-uptype') || '', option.multiple = option.element.data('multiple') > 0;
  10. /*! 文件选择筛选 */
  11. $((option.element.data('type') || '').split(',')).map(function (i, ext) {
  12. if (allowMime[ext]) option.exts.push(ext), option.mimes.push(allowMime[ext]);
  13. });
  14. /*! 初始化上传组件 */
  15. option.uploader = layui.upload.render({
  16. auto: false, elem: element, accept: 'file', multiple: option.multiple,
  17. exts: option.exts.join('|'), acceptMime: option.mimes.join(','), choose: function (object) {
  18. option.element.triggerHandler('upload.choose', option.files = object.pushFile());
  19. option.uploader.config.elem.next().val(''), layui.each(option.files, function (index, file) {
  20. if (option.size > 0 && file.size > option.size) {
  21. return delete option.files[index], $.msg.tips('文件大小超出上传限制!');
  22. }
  23. option.load = option.hload || $.msg.loading('上传进度 <span data-upload-progress>0%</span>');
  24. option.count.total++, file.index = index, option.cache[index] = file, delete option.files[index];
  25. md5file(file).then(function (file) {
  26. option.element.triggerHandler('upload.hash', file);
  27. jQuery.ajax("{:url('admin/api.upload/state')}", {
  28. data: {xkey: file.xkey, uptype: option.uptype, safe: option.safe, name: file.name}, method: 'post', success: function (ret) {
  29. file.xurl = ret.data.url;
  30. if (parseInt(ret.code) === 404) {
  31. option.uploader.config.url = ret.data.server;
  32. option.uploader.config.data.key = ret.data.xkey;
  33. option.uploader.config.data.safe = ret.data.safe;
  34. option.uploader.config.data.uptype = ret.data.uptype;
  35. if (ret.data.uptype === 'qiniu') {
  36. option.uploader.config.data.token = ret.data.token;
  37. } else if (ret.data.uptype === 'alioss') {
  38. option.uploader.config.data['policy'] = ret.data.policy;
  39. option.uploader.config.data['signature'] = ret.data.signature;
  40. option.uploader.config.data['OSSAccessKeyId'] = ret.data.OSSAccessKeyId;
  41. option.uploader.config.data['success_action_status'] = 200;
  42. option.uploader.config.data['Content-Disposition'] = 'inline;filename=' + encodeURIComponent(file.name);
  43. } else if (ret.data.uptype === 'txcos') {
  44. option.uploader.config.data['q-ak'] = ret.data['q-ak'];
  45. option.uploader.config.data['policy'] = ret.data.policy;
  46. option.uploader.config.data['q-key-time'] = ret.data['q-key-time'];
  47. option.uploader.config.data['q-signature'] = ret.data['q-signature'];
  48. option.uploader.config.data['q-sign-algorithm'] = ret.data['q-sign-algorithm'];
  49. option.uploader.config.data['success_action_status'] = 200;
  50. option.uploader.config.data['Content-Disposition'] = 'inline;filename=' + encodeURIComponent(file.name);
  51. }
  52. object.upload(file.index, file);
  53. } else if (parseInt(ret.code) === 200) {
  54. option.uploader.config.done({uploaded: true, url: file.xurl}, file.index);
  55. } else {
  56. $.msg.tips(ret.info || ret.error.message || '文件上传出错!');
  57. }
  58. }
  59. });
  60. });
  61. });
  62. }, progress: function (number) {
  63. $('[data-upload-progress]').html(number + '%');
  64. option.element.triggerHandler('upload.progress', {event: arguments[2], file: arguments[3]});
  65. }, done: function (ret, index) {
  66. option.element.triggerHandler('upload.done', {file: option.cache[index], data: ret});
  67. if (++option.count.uploaded >= option.count.total) {
  68. option.element.triggerHandler('upload.complete', {});
  69. option.element.html(option.element.data('html'));
  70. option.hload || $.msg.close(option.load);
  71. option.uploader.reload();
  72. }
  73. if (typeof ret.code === 'number' && parseInt(ret.code) === 0) {
  74. return $.msg.tips(ret.info || '文件上传失败!');
  75. }
  76. if (typeof option.cache[index].xurl !== 'string') {
  77. return $.msg.tips('无效的文件对象!');
  78. }
  79. if (typeof ret.uploaded === 'undefined' && typeof option.cache[index].xurl === 'string') {
  80. ret = {uploaded: true, url: option.cache[index].xurl};
  81. }
  82. if (ret.uploaded) {
  83. if (typeof callable === 'function') {
  84. callable.call(option.element, ret.url, option.cache[index]);
  85. } else {
  86. option.input.val(ret.url).trigger('change');
  87. }
  88. } else {
  89. $.msg.tips(ret.info || ret.error.message || '文件上传出错!');
  90. }
  91. }
  92. });
  93. };
  94. function md5file(file) {
  95. var deferred = jQuery.Deferred();
  96. file.xext = file.name.indexOf('.') > -1 ? file.name.split('.').pop() : 'tmp';
  97. /*! 兼容不能计算文件 HASH 的情况 */
  98. if (!window.FileReader) return jQuery.when((function (date, chars) {
  99. date = new Date(), chars = 'abcdefhijkmnprstwxyz0123456789';
  100. this.xmd5 = '' + date.getFullYear() + (date.getMonth() + 1) + date.getDay() + date.getHours() + date.getMinutes() + date.getSeconds();
  101. while (this.xmd5.length < 32) this.xmd5 += chars.charAt(Math.floor(Math.random() * chars.length));
  102. return setFileXdata(file, this.xmd5), deferred.resolve(file, file.xmd5, file.xkey), deferred;
  103. }).call(this));
  104. /*! 读取文件并计算 HASH 值 */
  105. var spark = new SparkMD5.ArrayBuffer();
  106. var slice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
  107. file.chunkIdx = 0, file.chunkSize = 2097152, file.chunkTotal = Math.ceil(this.size / this.chunkSize);
  108. return jQuery.when(loadNextChunk(file));
  109. function setFileXdata(file, xmd5) {
  110. file.xmd5 = xmd5, file.xkey = file.xmd5.substr(0, 2) + '/' + file.xmd5.substr(2, 30) + '.' + file.xext;
  111. return delete file.chunkIdx, delete file.chunkSize, delete file.chunkTotal, file;
  112. }
  113. function loadNextChunk(file) {
  114. this.reader = new FileReader();
  115. this.reader.onload = function (event) {
  116. spark.append(event.target.result);
  117. if (++file.chunkIdx < file.chunkTotal) {
  118. loadNextChunk(file);
  119. } else {
  120. setFileXdata(file, spark.end());
  121. deferred.resolve(file, file.xmd5, file.xkey);
  122. }
  123. };
  124. this.reader.onerror = function () {
  125. deferred.reject();
  126. };
  127. this.start = file.chunkIdx * file.chunkSize;
  128. this.loaded = (this.start + file.chunkSize >= file.size) ? file.size : this.start + file.chunkSize;
  129. this.reader.readAsArrayBuffer(slice.call(file, this.start, this.loaded));
  130. return deferred.notify(file, (this.loaded / file.size * 100).toFixed(2)), deferred;
  131. }
  132. }
  133. });