upload.js 9.1 KB

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