当前位置:   article > 正文

uniapp微信小程序实现大文件上传 分片上传 进度条_miniprogram-file-uploader

miniprogram-file-uploader

一、安装  

npm i miniprogram-file-uploader

二、页面引入

    import Uploader from 'miniprogram-file-uploader'

 三、实现功能(重要)

        1.获取图片的路径

        2.设置分片的大小

        3.将数据放入

  1. let obj = this.uploadItem //图片路径或者视频路径 可以通过chooseMedia的api进行获取
  2. var tempFilePath = obj.tempFilePath
  3. var file = {//重点,分片要的参数
  4. ext_file_name: '',
  5. index: 0,
  6. chunkSize: 1024 * 1024 * 0.5 //分片0.5M 根据自己的分片需求设置一片多大
  7. }
  8. file.ext_file_name = obj.tempFilePath
  9. file.index = Math.ceil(obj.size / file.chunkSize) //获取索引
  10. var opt = {
  11. fileName: file.ext_file_name,
  12. totalSize: obj.size,
  13. chunkSize: file.chunkSize,
  14. query: { //后端需要的参数
  15. activity_id: this.activity_id,
  16. file_type: this.file_type,
  17. total_size: obj.size,
  18. ext_file_name: file.ext_file_name
  19. },
  20. timeout: 180000, //请求超时时间,默认 10000 ms
  21. verifyUrl: 'https://xxxxxxx/upload/b2b3.0/digital_activity/?act=verify',//后端提供接口路径
  22. uploadUrl: 'https://xxxxxxx/upload/b2b3.0/digital_activity/?act=upload',//后端提供接口路径
  23. mergeUrl: `https://xxxxxxx/upload/b2b3.0/digital_activity/?act=merge&activity_id=${this.activity_id}&file_type=${this.file_type}&ext_file_name=${file.ext_file_name}`, //合并分块 //后端提供接口路径 参数自己传
  24. tempFilePath: tempFilePath //图片路径
  25. }
  26. const uploader = new Uploader(opt)//在这里扭一下

四、实现上传进度条

  1. <view class="prompt_box" >
  2. <view class="prompt_top">
  3. <view class="prompt_title two_ti">正在提交</view>
  4. <view class="prompt_tow">
  5. <view class="">
  6. <progress :percent="upload.progress" stroke-width="4px" class="progress"
  7. :activeColor='theme_config.theme_color' show-info="true" font-size="15px"
  8. border-radius="4px"></progress>
  9. </view>
  10. <view class="">正在上传,请耐心等待</view>
  11. </view>
  12. </view>
  13. <view class="prompt_but two" @click="CancelSubmission">
  14. <view>取消提交</view>
  15. </view>
  16. </view>
  1. data(){
  2. return{
  3. upload: { //上传进度
  4. // eq: -1,
  5. size: 0,
  6. progress: 0,
  7. uploadedSize: 0,
  8. averageSpeed: 0,
  9. timeRemaining: 0
  10. },
  11. }
  12. }
  13. //js------
  14. const uploader = new Uploader(opt)//在这里扭一下
  15. uploader.on('complete', (res) => {})
  16. uploader.on('retry', (res) => {})
  17. uploader.on('success', (res) => {
  18. if (res.result.status_info.status_code == 100) {
  19. this.PostSubmitWork(res.result.build_info)
  20. }
  21. })
  22. uploader.on('fail', (res) => {
  23. this.isupload = false
  24. })
  25. uploader.on('progress', (res) => { //上传进度这里是进度条提供
  26. var tmp = {
  27. size: obj.size / 1024,
  28. progress: res.progress,
  29. uploadedSize: parseInt(res.uploadedSize / 1024),
  30. averageSpeed: parseInt(res.averageSpeed / 1024),
  31. timeRemaining: res.timeRemaining / 1000
  32. }
  33. this.upload = tmp//进度的所有数据
  34. })

全部函数js

  1. // 上传逻辑
  2. uploadOne() {
  3. let obj = this.uploadItem //图片路径或者视频路径 可以通过chooseMedia的api进行获取
  4. var tempFilePath = obj.tempFilePath
  5. var file = {//重点,分片要的参数
  6. ext_file_name: '',
  7. index: 0,
  8. chunkSize: 1024 * 1024 * 0.5 //分片0.5M 根据自己的分片需求设置一片多大
  9. }
  10. file.ext_file_name = obj.tempFilePath
  11. file.index = Math.ceil(obj.size / file.chunkSize) //获取索引
  12. var opt = {
  13. fileName: file.ext_file_name,
  14. totalSize: obj.size,
  15. chunkSize: file.chunkSize,
  16. query: { //后端需要的参数
  17. activity_id: this.activity_id,
  18. file_type: this.file_type,
  19. total_size: obj.size,
  20. ext_file_name: file.ext_file_name
  21. },
  22. timeout: 180000, //请求超时时间,默认 10000 ms
  23. verifyUrl: 'https://xxxxxxx/upload/b2b3.0/digital_activity/?act=verify',//后端提供接口路径
  24. uploadUrl: 'https://xxxxxxx/upload/b2b3.0/digital_activity/?act=upload',//后端提供接口路径
  25. mergeUrl: `https://xxxxxxx/upload/b2b3.0/digital_activity/?act=merge&activity_id=${this.activity_id}&file_type=${this.file_type}&ext_file_name=${file.ext_file_name}`, //合并分块 //后端提供接口路径 参数自己传
  26. tempFilePath: tempFilePath //图片路径
  27. }
  28. const uploader = new Uploader(opt)//在这里扭一下
  29. // 成功或失败都会触发
  30. uploader.on('complete', (res) => {})
  31. uploader.on('retry', (res) => {})
  32. uploader.on('success', (res) => {
  33. if (res.result.status_info.status_code == 100) {
  34. this.PostSubmitWork(res.result.build_info)
  35. }
  36. })
  37. uploader.on('fail', (res) => {
  38. this.isupload = false
  39. })
  40. uploader.on('progress', (res) => { //上传进度这里是进度条提供
  41. var tmp = {
  42. size: obj.size / 1024,
  43. progress: res.progress,
  44. uploadedSize: parseInt(res.uploadedSize / 1024),
  45. averageSpeed: parseInt(res.averageSpeed / 1024),
  46. timeRemaining: res.timeRemaining / 1000
  47. }
  48. this.upload = tmp
  49. })
  50. uploader.upload()
  51. this.uploader = uploader
  52. },

五、修改npm的源码,处理请求源码中请求所携带的参数问题,以及报错处理(重要)

修改npm后的源码。可直接用(需修改请求参数)
 

  1. /**
  2. * miniprogram-uploader 1.0.0
  3. * description: A JavaScript library supports miniprogram to upload large file.
  4. * author: sanfordsun
  5. * Released under the MIT License.
  6. */
  7. import md5 from 'js-md5'
  8. var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window :
  9. typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  10. function createCommonjsModule(fn, basedir, module) {
  11. return module = {
  12. path: basedir,
  13. exports: {},
  14. require: function(path, base) {
  15. return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
  16. }
  17. }, fn(module, module.exports), module.exports;
  18. }
  19. function commonjsRequire() {
  20. throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
  21. }
  22. var logger = createCommonjsModule(function(module) {
  23. /*!
  24. * js-logger - http://github.com/jonnyreeves/js-logger
  25. * Jonny Reeves, http://jonnyreeves.co.uk/
  26. * js-logger may be freely distributed under the MIT license.
  27. */
  28. (function(global) {
  29. // Top level module for the global, static logger instance.
  30. var Logger = {};
  31. // For those that are at home that are keeping score.
  32. Logger.VERSION = "1.6.0";
  33. // Function which handles all incoming log messages.
  34. var logHandler;
  35. // Map of ContextualLogger instances by name; used by Logger.get() to return the same named instance.
  36. var contextualLoggersByNameMap = {};
  37. // Polyfill for ES5's Function.bind.
  38. var bind = function(scope, func) {
  39. return function() {
  40. return func.apply(scope, arguments);
  41. };
  42. };
  43. // Super exciting object merger-matron 9000 adding another 100 bytes to your download.
  44. var merge = function() {
  45. var args = arguments,
  46. target = args[0],
  47. key, i;
  48. for (i = 1; i < args.length; i++) {
  49. for (key in args[i]) {
  50. if (!(key in target) && args[i].hasOwnProperty(key)) {
  51. target[key] = args[i][key];
  52. }
  53. }
  54. }
  55. return target;
  56. };
  57. // Helper to define a logging level object; helps with optimisation.
  58. var defineLogLevel = function(value, name) {
  59. return {
  60. value: value,
  61. name: name
  62. };
  63. };
  64. // Predefined logging levels.
  65. Logger.TRACE = defineLogLevel(1, 'TRACE');
  66. Logger.DEBUG = defineLogLevel(2, 'DEBUG');
  67. Logger.INFO = defineLogLevel(3, 'INFO');
  68. Logger.TIME = defineLogLevel(4, 'TIME');
  69. Logger.WARN = defineLogLevel(5, 'WARN');
  70. Logger.ERROR = defineLogLevel(8, 'ERROR');
  71. Logger.OFF = defineLogLevel(99, 'OFF');
  72. // Inner class which performs the bulk of the work; ContextualLogger instances can be configured independently
  73. // of each other.
  74. var ContextualLogger = function(defaultContext) {
  75. this.context = defaultContext;
  76. this.setLevel(defaultContext.filterLevel);
  77. this.log = this.info; // Convenience alias.
  78. };
  79. ContextualLogger.prototype = {
  80. // Changes the current logging level for the logging instance.
  81. setLevel: function(newLevel) {
  82. // Ensure the supplied Level object looks valid.
  83. if (newLevel && "value" in newLevel) {
  84. this.context.filterLevel = newLevel;
  85. }
  86. },
  87. // Gets the current logging level for the logging instance
  88. getLevel: function() {
  89. return this.context.filterLevel;
  90. },
  91. // Is the logger configured to output messages at the supplied level?
  92. enabledFor: function(lvl) {
  93. var filterLevel = this.context.filterLevel;
  94. return lvl.value >= filterLevel.value;
  95. },
  96. trace: function() {
  97. this.invoke(Logger.TRACE, arguments);
  98. },
  99. debug: function() {
  100. this.invoke(Logger.DEBUG, arguments);
  101. },
  102. info: function() {
  103. this.invoke(Logger.INFO, arguments);
  104. },
  105. warn: function() {
  106. this.invoke(Logger.WARN, arguments);
  107. },
  108. error: function() {
  109. this.invoke(Logger.ERROR, arguments);
  110. },
  111. time: function(label) {
  112. if (typeof label === 'string' && label.length > 0) {
  113. this.invoke(Logger.TIME, [label, 'start']);
  114. }
  115. },
  116. timeEnd: function(label) {
  117. if (typeof label === 'string' && label.length > 0) {
  118. this.invoke(Logger.TIME, [label, 'end']);
  119. }
  120. },
  121. // Invokes the logger callback if it's not being filtered.
  122. invoke: function(level, msgArgs) {
  123. if (logHandler && this.enabledFor(level)) {
  124. logHandler(msgArgs, merge({
  125. level: level
  126. }, this.context));
  127. }
  128. }
  129. };
  130. // Protected instance which all calls to the to level `Logger` module will be routed through.
  131. var globalLogger = new ContextualLogger({
  132. filterLevel: Logger.OFF
  133. });
  134. // Configure the global Logger instance.
  135. (function() {
  136. // Shortcut for optimisers.
  137. var L = Logger;
  138. L.enabledFor = bind(globalLogger, globalLogger.enabledFor);
  139. L.trace = bind(globalLogger, globalLogger.trace);
  140. L.debug = bind(globalLogger, globalLogger.debug);
  141. L.time = bind(globalLogger, globalLogger.time);
  142. L.timeEnd = bind(globalLogger, globalLogger.timeEnd);
  143. L.info = bind(globalLogger, globalLogger.info);
  144. L.warn = bind(globalLogger, globalLogger.warn);
  145. L.error = bind(globalLogger, globalLogger.error);
  146. // Don't forget the convenience alias!
  147. L.log = L.info;
  148. }());
  149. // Set the global logging handler. The supplied function should expect two arguments, the first being an arguments
  150. // object with the supplied log messages and the second being a context object which contains a hash of stateful
  151. // parameters which the logging function can consume.
  152. Logger.setHandler = function(func) {
  153. logHandler = func;
  154. };
  155. // Sets the global logging filter level which applies to *all* previously registered, and future Logger instances.
  156. // (note that named loggers (retrieved via `Logger.get`) can be configured independently if required).
  157. Logger.setLevel = function(level) {
  158. // Set the globalLogger's level.
  159. globalLogger.setLevel(level);
  160. // Apply this level to all registered contextual loggers.
  161. for (var key in contextualLoggersByNameMap) {
  162. if (contextualLoggersByNameMap.hasOwnProperty(key)) {
  163. contextualLoggersByNameMap[key].setLevel(level);
  164. }
  165. }
  166. };
  167. // Gets the global logging filter level
  168. Logger.getLevel = function() {
  169. return globalLogger.getLevel();
  170. };
  171. // Retrieve a ContextualLogger instance. Note that named loggers automatically inherit the global logger's level,
  172. // default context and log handler.
  173. Logger.get = function(name) {
  174. // All logger instances are cached so they can be configured ahead of use.
  175. return contextualLoggersByNameMap[name] ||
  176. (contextualLoggersByNameMap[name] = new ContextualLogger(merge({
  177. name: name
  178. }, globalLogger.context)));
  179. };
  180. // CreateDefaultHandler returns a handler function which can be passed to `Logger.setHandler()` which will
  181. // write to the window's console object (if present); the optional options object can be used to customise the
  182. // formatter used to format each log message.
  183. Logger.createDefaultHandler = function(options) {
  184. options = options || {};
  185. options.formatter = options.formatter || function defaultMessageFormatter(messages, context) {
  186. // Prepend the logger's name to the log message for easy identification.
  187. if (context.name) {
  188. messages.unshift("[" + context.name + "]");
  189. }
  190. };
  191. // Map of timestamps by timer labels used to track `#time` and `#timeEnd()` invocations in environments
  192. // that don't offer a native console method.
  193. var timerStartTimeByLabelMap = {};
  194. // Support for IE8+ (and other, slightly more sane environments)
  195. var invokeConsoleMethod = function(hdlr, messages) {
  196. Function.prototype.apply.call(hdlr, console, messages);
  197. };
  198. // Check for the presence of a logger.
  199. if (typeof console === "undefined") {
  200. return function() {
  201. /* no console */
  202. };
  203. }
  204. return function(messages, context) {
  205. // Convert arguments object to Array.
  206. messages = Array.prototype.slice.call(messages);
  207. var hdlr = console.log;
  208. var timerLabel;
  209. if (context.level === Logger.TIME) {
  210. timerLabel = (context.name ? '[' + context.name + '] ' : '') + messages[0];
  211. if (messages[1] === 'start') {
  212. if (console.time) {
  213. console.time(timerLabel);
  214. } else {
  215. timerStartTimeByLabelMap[timerLabel] = new Date().getTime();
  216. }
  217. } else {
  218. if (console.timeEnd) {
  219. console.timeEnd(timerLabel);
  220. } else {
  221. invokeConsoleMethod(hdlr, [timerLabel + ': ' +
  222. (new Date().getTime() - timerStartTimeByLabelMap[timerLabel]) + 'ms'
  223. ]);
  224. }
  225. }
  226. } else {
  227. // Delegate through to custom warn/error loggers if present on the console.
  228. if (context.level === Logger.WARN && console.warn) {
  229. hdlr = console.warn;
  230. } else if (context.level === Logger.ERROR && console.error) {
  231. hdlr = console.error;
  232. } else if (context.level === Logger.INFO && console.info) {
  233. hdlr = console.info;
  234. } else if (context.level === Logger.DEBUG && console.debug) {
  235. hdlr = console.debug;
  236. } else if (context.level === Logger.TRACE && console.trace) {
  237. hdlr = console.trace;
  238. }
  239. options.formatter(messages, context);
  240. invokeConsoleMethod(hdlr, messages);
  241. }
  242. };
  243. };
  244. // Configure and example a Default implementation which writes to the `window.console` (if present). The
  245. // `options` hash can be used to configure the default logLevel and provide a custom message formatter.
  246. Logger.useDefaults = function(options) {
  247. Logger.setLevel(options && options.defaultLevel || Logger.DEBUG);
  248. Logger.setHandler(Logger.createDefaultHandler(options));
  249. };
  250. // Export to popular environments boilerplate.
  251. if (module.exports) {
  252. module.exports = Logger;
  253. } else {
  254. Logger._prevLogger = global.Logger;
  255. Logger.noConflict = function() {
  256. global.Logger = Logger._prevLogger;
  257. return Logger;
  258. };
  259. global.Logger = Logger;
  260. }
  261. }(commonjsGlobal));
  262. });
  263. var sparkMd5 = createCommonjsModule(function(module, exports) {
  264. (function(factory) {
  265. {
  266. // Node/CommonJS
  267. module.exports = factory();
  268. }
  269. }(function(undefined$1) {
  270. /*
  271. * Fastest md5 implementation around (JKM md5).
  272. * Credits: Joseph Myers
  273. *
  274. * @see http://www.myersdaily.org/joseph/javascript/md5-text.html
  275. * @see http://jsperf.com/md5-shootout/7
  276. */
  277. /* this function is much faster,
  278. so if possible we use it. Some IEs
  279. are the only ones I know of that
  280. need the idiotic second function,
  281. generated by an if clause. */
  282. var hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
  283. function md5cycle(x, k) {
  284. var a = x[0],
  285. b = x[1],
  286. c = x[2],
  287. d = x[3];
  288. a += (b & c | ~b & d) + k[0] - 680876936 | 0;
  289. a = (a << 7 | a >>> 25) + b | 0;
  290. d += (a & b | ~a & c) + k[1] - 389564586 | 0;
  291. d = (d << 12 | d >>> 20) + a | 0;
  292. c += (d & a | ~d & b) + k[2] + 606105819 | 0;
  293. c = (c << 17 | c >>> 15) + d | 0;
  294. b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
  295. b = (b << 22 | b >>> 10) + c | 0;
  296. a += (b & c | ~b & d) + k[4] - 176418897 | 0;
  297. a = (a << 7 | a >>> 25) + b | 0;
  298. d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
  299. d = (d << 12 | d >>> 20) + a | 0;
  300. c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
  301. c = (c << 17 | c >>> 15) + d | 0;
  302. b += (c & d | ~c & a) + k[7] - 45705983 | 0;
  303. b = (b << 22 | b >>> 10) + c | 0;
  304. a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
  305. a = (a << 7 | a >>> 25) + b | 0;
  306. d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
  307. d = (d << 12 | d >>> 20) + a | 0;
  308. c += (d & a | ~d & b) + k[10] - 42063 | 0;
  309. c = (c << 17 | c >>> 15) + d | 0;
  310. b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
  311. b = (b << 22 | b >>> 10) + c | 0;
  312. a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
  313. a = (a << 7 | a >>> 25) + b | 0;
  314. d += (a & b | ~a & c) + k[13] - 40341101 | 0;
  315. d = (d << 12 | d >>> 20) + a | 0;
  316. c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
  317. c = (c << 17 | c >>> 15) + d | 0;
  318. b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
  319. b = (b << 22 | b >>> 10) + c | 0;
  320. a += (b & d | c & ~d) + k[1] - 165796510 | 0;
  321. a = (a << 5 | a >>> 27) + b | 0;
  322. d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
  323. d = (d << 9 | d >>> 23) + a | 0;
  324. c += (d & b | a & ~b) + k[11] + 643717713 | 0;
  325. c = (c << 14 | c >>> 18) + d | 0;
  326. b += (c & a | d & ~a) + k[0] - 373897302 | 0;
  327. b = (b << 20 | b >>> 12) + c | 0;
  328. a += (b & d | c & ~d) + k[5] - 701558691 | 0;
  329. a = (a << 5 | a >>> 27) + b | 0;
  330. d += (a & c | b & ~c) + k[10] + 38016083 | 0;
  331. d = (d << 9 | d >>> 23) + a | 0;
  332. c += (d & b | a & ~b) + k[15] - 660478335 | 0;
  333. c = (c << 14 | c >>> 18) + d | 0;
  334. b += (c & a | d & ~a) + k[4] - 405537848 | 0;
  335. b = (b << 20 | b >>> 12) + c | 0;
  336. a += (b & d | c & ~d) + k[9] + 568446438 | 0;
  337. a = (a << 5 | a >>> 27) + b | 0;
  338. d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
  339. d = (d << 9 | d >>> 23) + a | 0;
  340. c += (d & b | a & ~b) + k[3] - 187363961 | 0;
  341. c = (c << 14 | c >>> 18) + d | 0;
  342. b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
  343. b = (b << 20 | b >>> 12) + c | 0;
  344. a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
  345. a = (a << 5 | a >>> 27) + b | 0;
  346. d += (a & c | b & ~c) + k[2] - 51403784 | 0;
  347. d = (d << 9 | d >>> 23) + a | 0;
  348. c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
  349. c = (c << 14 | c >>> 18) + d | 0;
  350. b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
  351. b = (b << 20 | b >>> 12) + c | 0;
  352. a += (b ^ c ^ d) + k[5] - 378558 | 0;
  353. a = (a << 4 | a >>> 28) + b | 0;
  354. d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
  355. d = (d << 11 | d >>> 21) + a | 0;
  356. c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
  357. c = (c << 16 | c >>> 16) + d | 0;
  358. b += (c ^ d ^ a) + k[14] - 35309556 | 0;
  359. b = (b << 23 | b >>> 9) + c | 0;
  360. a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
  361. a = (a << 4 | a >>> 28) + b | 0;
  362. d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
  363. d = (d << 11 | d >>> 21) + a | 0;
  364. c += (d ^ a ^ b) + k[7] - 155497632 | 0;
  365. c = (c << 16 | c >>> 16) + d | 0;
  366. b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
  367. b = (b << 23 | b >>> 9) + c | 0;
  368. a += (b ^ c ^ d) + k[13] + 681279174 | 0;
  369. a = (a << 4 | a >>> 28) + b | 0;
  370. d += (a ^ b ^ c) + k[0] - 358537222 | 0;
  371. d = (d << 11 | d >>> 21) + a | 0;
  372. c += (d ^ a ^ b) + k[3] - 722521979 | 0;
  373. c = (c << 16 | c >>> 16) + d | 0;
  374. b += (c ^ d ^ a) + k[6] + 76029189 | 0;
  375. b = (b << 23 | b >>> 9) + c | 0;
  376. a += (b ^ c ^ d) + k[9] - 640364487 | 0;
  377. a = (a << 4 | a >>> 28) + b | 0;
  378. d += (a ^ b ^ c) + k[12] - 421815835 | 0;
  379. d = (d << 11 | d >>> 21) + a | 0;
  380. c += (d ^ a ^ b) + k[15] + 530742520 | 0;
  381. c = (c << 16 | c >>> 16) + d | 0;
  382. b += (c ^ d ^ a) + k[2] - 995338651 | 0;
  383. b = (b << 23 | b >>> 9) + c | 0;
  384. a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
  385. a = (a << 6 | a >>> 26) + b | 0;
  386. d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
  387. d = (d << 10 | d >>> 22) + a | 0;
  388. c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
  389. c = (c << 15 | c >>> 17) + d | 0;
  390. b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
  391. b = (b << 21 | b >>> 11) + c | 0;
  392. a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
  393. a = (a << 6 | a >>> 26) + b | 0;
  394. d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
  395. d = (d << 10 | d >>> 22) + a | 0;
  396. c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
  397. c = (c << 15 | c >>> 17) + d | 0;
  398. b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
  399. b = (b << 21 | b >>> 11) + c | 0;
  400. a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
  401. a = (a << 6 | a >>> 26) + b | 0;
  402. d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
  403. d = (d << 10 | d >>> 22) + a | 0;
  404. c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
  405. c = (c << 15 | c >>> 17) + d | 0;
  406. b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
  407. b = (b << 21 | b >>> 11) + c | 0;
  408. a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
  409. a = (a << 6 | a >>> 26) + b | 0;
  410. d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
  411. d = (d << 10 | d >>> 22) + a | 0;
  412. c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
  413. c = (c << 15 | c >>> 17) + d | 0;
  414. b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
  415. b = (b << 21 | b >>> 11) + c | 0;
  416. x[0] = a + x[0] | 0;
  417. x[1] = b + x[1] | 0;
  418. x[2] = c + x[2] | 0;
  419. x[3] = d + x[3] | 0;
  420. }
  421. function md5blk(s) {
  422. var md5blks = [],
  423. i; /* Andy King said do it this way. */
  424. for (i = 0; i < 64; i += 4) {
  425. md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s
  426. .charCodeAt(i + 3) << 24);
  427. }
  428. return md5blks;
  429. }
  430. function md5blk_array(a) {
  431. var md5blks = [],
  432. i; /* Andy King said do it this way. */
  433. for (i = 0; i < 64; i += 4) {
  434. md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
  435. }
  436. return md5blks;
  437. }
  438. function md51(s) {
  439. var n = s.length,
  440. state = [1732584193, -271733879, -1732584194, 271733878],
  441. i,
  442. length,
  443. tail,
  444. tmp,
  445. lo,
  446. hi;
  447. for (i = 64; i <= n; i += 64) {
  448. md5cycle(state, md5blk(s.substring(i - 64, i)));
  449. }
  450. s = s.substring(i - 64);
  451. length = s.length;
  452. tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  453. for (i = 0; i < length; i += 1) {
  454. tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
  455. }
  456. tail[i >> 2] |= 0x80 << ((i % 4) << 3);
  457. if (i > 55) {
  458. md5cycle(state, tail);
  459. for (i = 0; i < 16; i += 1) {
  460. tail[i] = 0;
  461. }
  462. }
  463. // Beware that the final length might not fit in 32 bits so we take care of that
  464. tmp = n * 8;
  465. tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
  466. lo = parseInt(tmp[2], 16);
  467. hi = parseInt(tmp[1], 16) || 0;
  468. tail[14] = lo;
  469. tail[15] = hi;
  470. md5cycle(state, tail);
  471. return state;
  472. }
  473. function md51_array(a) {
  474. var n = a.length,
  475. state = [1732584193, -271733879, -1732584194, 271733878],
  476. i,
  477. length,
  478. tail,
  479. tmp,
  480. lo,
  481. hi;
  482. for (i = 64; i <= n; i += 64) {
  483. md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
  484. }
  485. // Not sure if it is a bug, however IE10 will always produce a sub array of length 1
  486. // containing the last element of the parent array if the sub array specified starts
  487. // beyond the length of the parent array - weird.
  488. // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
  489. a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);
  490. length = a.length;
  491. tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  492. for (i = 0; i < length; i += 1) {
  493. tail[i >> 2] |= a[i] << ((i % 4) << 3);
  494. }
  495. tail[i >> 2] |= 0x80 << ((i % 4) << 3);
  496. if (i > 55) {
  497. md5cycle(state, tail);
  498. for (i = 0; i < 16; i += 1) {
  499. tail[i] = 0;
  500. }
  501. }
  502. // Beware that the final length might not fit in 32 bits so we take care of that
  503. tmp = n * 8;
  504. tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
  505. lo = parseInt(tmp[2], 16);
  506. hi = parseInt(tmp[1], 16) || 0;
  507. tail[14] = lo;
  508. tail[15] = hi;
  509. md5cycle(state, tail);
  510. return state;
  511. }
  512. function rhex(n) {
  513. var s = '',
  514. j;
  515. for (j = 0; j < 4; j += 1) {
  516. s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
  517. }
  518. return s;
  519. }
  520. function hex(x) {
  521. var i;
  522. for (i = 0; i < x.length; i += 1) {
  523. x[i] = rhex(x[i]);
  524. }
  525. return x.join('');
  526. }
  527. // In some cases the fast add32 function cannot be used..
  528. if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592');
  529. // ---------------------------------------------------
  530. /**
  531. * ArrayBuffer slice polyfill.
  532. *
  533. * @see https://github.com/ttaubert/node-arraybuffer-slice
  534. */
  535. if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
  536. (function() {
  537. function clamp(val, length) {
  538. val = (val | 0) || 0;
  539. if (val < 0) {
  540. return Math.max(val + length, 0);
  541. }
  542. return Math.min(val, length);
  543. }
  544. ArrayBuffer.prototype.slice = function(from, to) {
  545. var length = this.byteLength,
  546. begin = clamp(from, length),
  547. end = length,
  548. num,
  549. target,
  550. targetArray,
  551. sourceArray;
  552. if (to !== undefined$1) {
  553. end = clamp(to, length);
  554. }
  555. if (begin > end) {
  556. return new ArrayBuffer(0);
  557. }
  558. num = end - begin;
  559. target = new ArrayBuffer(num);
  560. targetArray = new Uint8Array(target);
  561. sourceArray = new Uint8Array(this, begin, num);
  562. targetArray.set(sourceArray);
  563. return target;
  564. };
  565. })();
  566. }
  567. // ---------------------------------------------------
  568. /**
  569. * Helpers.
  570. */
  571. function toUtf8(str) {
  572. if (/[\u0080-\uFFFF]/.test(str)) {
  573. str = unescape(encodeURIComponent(str));
  574. }
  575. return str;
  576. }
  577. function utf8Str2ArrayBuffer(str, returnUInt8Array) {
  578. var length = str.length,
  579. buff = new ArrayBuffer(length),
  580. arr = new Uint8Array(buff),
  581. i;
  582. for (i = 0; i < length; i += 1) {
  583. arr[i] = str.charCodeAt(i);
  584. }
  585. return returnUInt8Array ? arr : buff;
  586. }
  587. function arrayBuffer2Utf8Str(buff) {
  588. return String.fromCharCode.apply(null, new Uint8Array(buff));
  589. }
  590. function concatenateArrayBuffers(first, second, returnUInt8Array) {
  591. var result = new Uint8Array(first.byteLength + second.byteLength);
  592. result.set(new Uint8Array(first));
  593. result.set(new Uint8Array(second), first.byteLength);
  594. return returnUInt8Array ? result : result.buffer;
  595. }
  596. function hexToBinaryString(hex) {
  597. var bytes = [],
  598. length = hex.length,
  599. x;
  600. for (x = 0; x < length - 1; x += 2) {
  601. bytes.push(parseInt(hex.substr(x, 2), 16));
  602. }
  603. return String.fromCharCode.apply(String, bytes);
  604. }
  605. // ---------------------------------------------------
  606. /**
  607. * SparkMD5 OOP implementation.
  608. *
  609. * Use this class to perform an incremental md5, otherwise use the
  610. * static methods instead.
  611. */
  612. function SparkMD5() {
  613. // call reset to init the instance
  614. this.reset();
  615. }
  616. /**
  617. * Appends a string.
  618. * A conversion will be applied if an utf8 string is detected.
  619. *
  620. * @param {String} str The string to be appended
  621. *
  622. * @return {SparkMD5} The instance itself
  623. */
  624. SparkMD5.prototype.append = function(str) {
  625. // Converts the string to utf8 bytes if necessary
  626. // Then append as binary
  627. this.appendBinary(toUtf8(str));
  628. return this;
  629. };
  630. /**
  631. * Appends a binary string.
  632. *
  633. * @param {String} contents The binary string to be appended
  634. *
  635. * @return {SparkMD5} The instance itself
  636. */
  637. SparkMD5.prototype.appendBinary = function(contents) {
  638. this._buff += contents;
  639. this._length += contents.length;
  640. var length = this._buff.length,
  641. i;
  642. for (i = 64; i <= length; i += 64) {
  643. md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
  644. }
  645. this._buff = this._buff.substring(i - 64);
  646. return this;
  647. };
  648. /**
  649. * Finishes the incremental computation, reseting the internal state and
  650. * returning the result.
  651. *
  652. * @param {Boolean} raw True to get the raw string, false to get the hex string
  653. *
  654. * @return {String} The result
  655. */
  656. SparkMD5.prototype.end = function(raw) {
  657. var buff = this._buff,
  658. length = buff.length,
  659. i,
  660. tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  661. ret;
  662. for (i = 0; i < length; i += 1) {
  663. tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
  664. }
  665. this._finish(tail, length);
  666. ret = hex(this._hash);
  667. if (raw) {
  668. ret = hexToBinaryString(ret);
  669. }
  670. this.reset();
  671. return ret;
  672. };
  673. /**
  674. * Resets the internal state of the computation.
  675. *
  676. * @return {SparkMD5} The instance itself
  677. */
  678. SparkMD5.prototype.reset = function() {
  679. this._buff = '';
  680. this._length = 0;
  681. this._hash = [1732584193, -271733879, -1732584194, 271733878];
  682. return this;
  683. };
  684. /**
  685. * Gets the internal state of the computation.
  686. *
  687. * @return {Object} The state
  688. */
  689. SparkMD5.prototype.getState = function() {
  690. return {
  691. buff: this._buff,
  692. length: this._length,
  693. hash: this._hash.slice()
  694. };
  695. };
  696. /**
  697. * Gets the internal state of the computation.
  698. *
  699. * @param {Object} state The state
  700. *
  701. * @return {SparkMD5} The instance itself
  702. */
  703. SparkMD5.prototype.setState = function(state) {
  704. this._buff = state.buff;
  705. this._length = state.length;
  706. this._hash = state.hash;
  707. return this;
  708. };
  709. /**
  710. * Releases memory used by the incremental buffer and other additional
  711. * resources. If you plan to use the instance again, use reset instead.
  712. */
  713. SparkMD5.prototype.destroy = function() {
  714. delete this._hash;
  715. delete this._buff;
  716. delete this._length;
  717. };
  718. /**
  719. * Finish the final calculation based on the tail.
  720. *
  721. * @param {Array} tail The tail (will be modified)
  722. * @param {Number} length The length of the remaining buffer
  723. */
  724. SparkMD5.prototype._finish = function(tail, length) {
  725. var i = length,
  726. tmp,
  727. lo,
  728. hi;
  729. tail[i >> 2] |= 0x80 << ((i % 4) << 3);
  730. if (i > 55) {
  731. md5cycle(this._hash, tail);
  732. for (i = 0; i < 16; i += 1) {
  733. tail[i] = 0;
  734. }
  735. }
  736. // Do the final computation based on the tail and length
  737. // Beware that the final length may not fit in 32 bits so we take care of that
  738. tmp = this._length * 8;
  739. tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
  740. lo = parseInt(tmp[2], 16);
  741. hi = parseInt(tmp[1], 16) || 0;
  742. tail[14] = lo;
  743. tail[15] = hi;
  744. md5cycle(this._hash, tail);
  745. };
  746. /**
  747. * Performs the md5 hash on a string.
  748. * A conversion will be applied if utf8 string is detected.
  749. *
  750. * @param {String} str The string
  751. * @param {Boolean} [raw] True to get the raw string, false to get the hex string
  752. *
  753. * @return {String} The result
  754. */
  755. SparkMD5.hash = function(str, raw) {
  756. // Converts the string to utf8 bytes if necessary
  757. // Then compute it using the binary function
  758. return SparkMD5.hashBinary(toUtf8(str), raw);
  759. };
  760. /**
  761. * Performs the md5 hash on a binary string.
  762. *
  763. * @param {String} content The binary string
  764. * @param {Boolean} [raw] True to get the raw string, false to get the hex string
  765. *
  766. * @return {String} The result
  767. */
  768. SparkMD5.hashBinary = function(content, raw) {
  769. var hash = md51(content),
  770. ret = hex(hash);
  771. return raw ? hexToBinaryString(ret) : ret;
  772. };
  773. // ---------------------------------------------------
  774. /**
  775. * SparkMD5 OOP implementation for array buffers.
  776. *
  777. * Use this class to perform an incremental md5 ONLY for array buffers.
  778. */
  779. SparkMD5.ArrayBuffer = function() {
  780. // call reset to init the instance
  781. this.reset();
  782. };
  783. /**
  784. * Appends an array buffer.
  785. *
  786. * @param {ArrayBuffer} arr The array to be appended
  787. *
  788. * @return {SparkMD5.ArrayBuffer} The instance itself
  789. */
  790. SparkMD5.ArrayBuffer.prototype.append = function(arr) {
  791. var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
  792. length = buff.length,
  793. i;
  794. this._length += arr.byteLength;
  795. for (i = 64; i <= length; i += 64) {
  796. md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
  797. }
  798. this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
  799. return this;
  800. };
  801. /**
  802. * Finishes the incremental computation, reseting the internal state and
  803. * returning the result.
  804. *
  805. * @param {Boolean} raw True to get the raw string, false to get the hex string
  806. *
  807. * @return {String} The result
  808. */
  809. SparkMD5.ArrayBuffer.prototype.end = function(raw) {
  810. var buff = this._buff,
  811. length = buff.length,
  812. tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  813. i,
  814. ret;
  815. for (i = 0; i < length; i += 1) {
  816. tail[i >> 2] |= buff[i] << ((i % 4) << 3);
  817. }
  818. this._finish(tail, length);
  819. ret = hex(this._hash);
  820. if (raw) {
  821. ret = hexToBinaryString(ret);
  822. }
  823. this.reset();
  824. return ret;
  825. };
  826. /**
  827. * Resets the internal state of the computation.
  828. *
  829. * @return {SparkMD5.ArrayBuffer} The instance itself
  830. */
  831. SparkMD5.ArrayBuffer.prototype.reset = function() {
  832. this._buff = new Uint8Array(0);
  833. this._length = 0;
  834. this._hash = [1732584193, -271733879, -1732584194, 271733878];
  835. return this;
  836. };
  837. /**
  838. * Gets the internal state of the computation.
  839. *
  840. * @return {Object} The state
  841. */
  842. SparkMD5.ArrayBuffer.prototype.getState = function() {
  843. var state = SparkMD5.prototype.getState.call(this);
  844. // Convert buffer to a string
  845. state.buff = arrayBuffer2Utf8Str(state.buff);
  846. return state;
  847. };
  848. /**
  849. * Gets the internal state of the computation.
  850. *
  851. * @param {Object} state The state
  852. *
  853. * @return {SparkMD5.ArrayBuffer} The instance itself
  854. */
  855. SparkMD5.ArrayBuffer.prototype.setState = function(state) {
  856. // Convert string to buffer
  857. state.buff = utf8Str2ArrayBuffer(state.buff, true);
  858. return SparkMD5.prototype.setState.call(this, state);
  859. };
  860. SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
  861. SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
  862. /**
  863. * Performs the md5 hash on an array buffer.
  864. *
  865. * @param {ArrayBuffer} arr The array buffer
  866. * @param {Boolean} [raw] True to get the raw string, false to get the hex one
  867. *
  868. * @return {String} The result
  869. */
  870. SparkMD5.ArrayBuffer.hash = function(arr, raw) {
  871. var hash = md51_array(new Uint8Array(arr)),
  872. ret = hex(hash);
  873. return raw ? hexToBinaryString(ret) : ret;
  874. };
  875. return SparkMD5;
  876. }));
  877. });
  878. var config = {
  879. tempFilePath: '',
  880. totalSize: 0,
  881. fileName: '',
  882. verifyUrl: '',
  883. uploadUrl: '',
  884. mergeUrl: '',
  885. maxConcurrency: 5,
  886. generateIdentifier: null,
  887. chunkSize: 5 * 1024 * 1024,
  888. maxMemory: 100 * 1024 * 1024,
  889. query: '',
  890. header: {},
  891. testChunks: false,
  892. chunkRetryInterval: 0,
  893. maxChunkRetries: 0,
  894. timeout: 10000,
  895. successStatus: [200, 201, 202],
  896. failStatus: [404, 415, 500, 501],
  897. verbose: false
  898. };
  899. class EventEmitter {
  900. constructor() {
  901. this.events = {};
  902. }
  903. on(event, listener) {
  904. if (typeof this.events[event] !== 'object') {
  905. this.events[event] = [];
  906. }
  907. this.events[event].push(listener);
  908. return () => this.off(event, listener)
  909. }
  910. off(event, listener) {
  911. if (typeof this.events[event] === 'object') {
  912. const idx = this.events[event].indexOf(listener);
  913. if (idx > -1) {
  914. this.events[event].splice(idx, 1);
  915. }
  916. }
  917. }
  918. emit(event, ...args) {
  919. if (typeof this.events[event] === 'object') {
  920. this.events[event].forEach(listener => listener.apply(this, args));
  921. }
  922. }
  923. once(event, listener) {
  924. const remove = this.on(event, (...args) => {
  925. remove();
  926. listener.apply(this, args);
  927. });
  928. }
  929. }
  930. const isFunction = x => typeof x === 'function';
  931. function promisify(func) {
  932. if (!isFunction(func)) return func
  933. return (args = {}) => new Promise((resolve, reject) => {
  934. func(
  935. Object.assign(args, {
  936. success: resolve,
  937. fail: reject
  938. })
  939. );
  940. })
  941. }
  942. function addParams(url = '', params = {}) {
  943. const parts = url.split('&');
  944. const query = Object.keys(params).map(key => `${key}=${params[key]}`).join('&');
  945. return query ? `${parts[0]}&${query}` : parts[0]
  946. }
  947. const awaitWrap = (promise) => promise
  948. .then(data => [null, data])
  949. .catch(err => [err, null]);
  950. const compareVersion = (v1, v2) => {
  951. v1 = v1.split('.');
  952. v2 = v2.split('.');
  953. const len = Math.max(v1.length, v2.length);
  954. while (v1.length < len) {
  955. v1.push('0');
  956. }
  957. while (v2.length < len) {
  958. v2.push('0');
  959. }
  960. for (let i = 0; i < len; i++) {
  961. const num1 = parseInt(v1[i], 10);
  962. const num2 = parseInt(v2[i], 10);
  963. if (num1 > num2) {
  964. return 1
  965. } else if (num1 < num2) {
  966. return -1
  967. }
  968. }
  969. return 0
  970. };
  971. logger.useDefaults({
  972. defaultLevel: logger.OFF,
  973. formatter(messages) {
  974. const now = new Date();
  975. const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
  976. messages.unshift(time);
  977. messages.unshift('[Uploader]');
  978. }
  979. });
  980. const fileManager = wx.getFileSystemManager();
  981. const readFileAsync = promisify(fileManager.readFile);
  982. const miniProgram = wx.getAccountInfoSync();
  983. const systemInfo = wx.getSystemInfoSync();
  984. const appId = miniProgram.appId;
  985. const MB = 1024 * 1024;
  986. // -----------------下面是请求的数据,可以删掉
  987. let app_key = 'xxx';
  988. let key = 'xxx';
  989. // 随机32位数
  990. function nonce() {
  991. let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B',
  992. 'C', 'D', 'E'
  993. ];
  994. let res = '';
  995. for (let i = 0; i < 32; i++) {
  996. let pos = Math.round(Math.random() * (arr.length - 1));
  997. res += arr[pos]
  998. }
  999. return res;
  1000. }
  1001. function signfn(action, postParam) {
  1002. // 时间戳
  1003. let ts = new Date().getTime().toString();
  1004. let randomNum = nonce();
  1005. let act = app_key; //请求带的url
  1006. function getSign(params, app_key, key) {
  1007. if (typeof params == "string") {
  1008. return paramsStrSort(params);
  1009. } else if (typeof params == "object") {
  1010. let arr = [];
  1011. for (let i in params) {
  1012. arr.push((i + "=" + params[i]));
  1013. }
  1014. return paramsStrSort(arr.join(("&")))
  1015. }
  1016. }
  1017. function paramsStrSort(paramsStr) {
  1018. let url = paramsStr;
  1019. let arr = [];
  1020. let p = url.split("&");
  1021. for (let i in p) {
  1022. let temp_arr = p[i].split("=");
  1023. arr.push((temp_arr[0].toLowerCase() + "=" + temp_arr[1]));
  1024. }
  1025. let urlStr = arr.sort().join("&");
  1026. let newUrl = urlStr + '&key=' + key;
  1027. return md5(newUrl);
  1028. }
  1029. var params = {
  1030. act,
  1031. 'ts': ts,
  1032. 'nonce': randomNum,
  1033. app_key
  1034. };
  1035. // 转成字符串
  1036. let signstr = JSON.stringify(params);
  1037. let sign = getSign(params, app_key, key);
  1038. return {
  1039. sign,
  1040. ts,
  1041. randomNum,
  1042. app_key
  1043. };
  1044. }
  1045. // -----------------上面是请求的数据,可以删掉 结束
  1046. class Uploader {
  1047. constructor(option = {}) {
  1048. if (option.verbose) logger.setLevel(logger.INFO);
  1049. logger.debug('construct option ', option);
  1050. this.config = Object.assign(config, option);
  1051. this.emitter = new EventEmitter();
  1052. this.totalSize = this.config.totalSize;
  1053. this.chunkSize = this.config.chunkSize;
  1054. this.tempFilePath = this.config.tempFilePath;
  1055. this.totalChunks = Math.ceil(this.totalSize / this.chunkSize);
  1056. this.maxLoadChunks = Math.floor(this.config.maxMemory / this.chunkSize);
  1057. this._event();
  1058. }
  1059. static isSupport() {
  1060. const version = systemInfo.SDKVersion;
  1061. return compareVersion(version, '2.10.0') >= 0
  1062. }
  1063. async upload() {
  1064. this._reset();
  1065. logger.info('start generateIdentifier');
  1066. // step1: 计算 identifier
  1067. try {
  1068. logger.time('[Uploader] generateIdentifier');
  1069. if (this.config.testChunks) {
  1070. this.identifier = await this.computeMD5();
  1071. } else {
  1072. this.identifier = this.generateIdentifier();
  1073. }
  1074. logger.timeEnd('[Uploader] generateIdentifier');
  1075. logger.debug('generateIdentifier ', this.identifier);
  1076. } catch (error) {
  1077. this.handleFail({
  1078. errCode: 10002,
  1079. errMsg: error.message
  1080. });
  1081. return
  1082. }
  1083. logger.info('generateIdentifier end');
  1084. // step2: 获取已上传分片
  1085. if (this.config.testChunks && this.config.verifyUrl) {
  1086. logger.info('start verify uploaded chunks');
  1087. logger.time('[Uploader] verifyRequest');
  1088. const [verifyErr, verifyResp] = await awaitWrap(this.verifyRequest());
  1089. logger.timeEnd('[Uploader] verifyRequest');
  1090. logger.debug('verifyRequest', verifyErr, verifyResp);
  1091. if (verifyErr) {
  1092. this.handleFail({
  1093. errCode: 20001,
  1094. errMsg: verifyErr.errMsg
  1095. });
  1096. return
  1097. }
  1098. const {
  1099. needUpload,
  1100. uploadedChunks,
  1101. } = verifyResp.data;
  1102. logger.info('verify uploaded chunks end');
  1103. // 秒传逻辑
  1104. // 找不到合成的文件
  1105. if (!needUpload) {
  1106. this.progress = 100;
  1107. this.timeRemaining = 0;
  1108. this.dispatchProgress();
  1109. this.emit('success', {
  1110. errCode: 0,
  1111. ...verifyResp.data
  1112. });
  1113. this.emit('complete', {
  1114. errCode: 0,
  1115. ...verifyResp.data
  1116. });
  1117. return
  1118. // 分片齐全,但没有合并
  1119. } else if (uploadedChunks.length === this.totalChunks) {
  1120. this.progress = 100;
  1121. this.timeRemaining = 0;
  1122. this.dispatchProgress();
  1123. this.emit('uploadDone');
  1124. return
  1125. } else {
  1126. this.chunksIndexNeedRead = this.chunksIndexNeedRead.filter(v => !uploadedChunks.includes(v));
  1127. this.chunksIndexNeedSend = this.chunksIndexNeedSend.filter(v => !uploadedChunks.includes(v));
  1128. this.uploadedChunks = uploadedChunks.sort();
  1129. }
  1130. }
  1131. this.chunksNeedSend = this.chunksIndexNeedSend.length;
  1132. this.sizeNeedSend = this.chunksNeedSend * this.chunkSize;
  1133. if (this.chunksIndexNeedSend.includes(this.totalChunks - 1)) {
  1134. this.sizeNeedSend -= (this.totalChunks * this.chunkSize - this.totalSize);
  1135. }
  1136. logger.debug(`
  1137. start upload
  1138. uploadedChunks: ${this.uploadedChunks},
  1139. chunksQueue: ${this.chunksQueue},
  1140. chunksIndexNeedRead: ${this.chunksIndexNeedRead},
  1141. chunksNeedSend: ${this.chunksIndexNeedSend},
  1142. sizeNeedSend: ${this.sizeNeedSend}
  1143. `);
  1144. logger.info('start upload chunks');
  1145. logger.time('[Uploader] uploadChunks');
  1146. // step3: 开始上传
  1147. this.isUploading = true;
  1148. this._upload();
  1149. }
  1150. _requestAsync(args = {}, callback) {
  1151. const {
  1152. chunkRetryInterval,
  1153. maxChunkRetries,
  1154. successStatus,
  1155. failStatus
  1156. } = this.config;
  1157. let retries = maxChunkRetries;
  1158. // args.url||
  1159. const signInfo = signfn(args.url)
  1160. return new Promise((resolve, reject) => {
  1161. let ts = new Date().getTime().toString();
  1162. const doRequest = () => {
  1163. const task = wx.request({
  1164. ...args,
  1165. header: {
  1166. "Content-Type": "multipart/form-data",
  1167. "auth-ts": signInfo.ts, //时间戳
  1168. "auth-access-token": uni.getStorageSync('user_info').access_token || '',
  1169. "auth-nonce": signInfo.randomNum, //随机字符串(32位)
  1170. "auth-sign": signInfo.sign, //签名
  1171. "auth-key": signInfo.app_key, //应用的APP_KEY
  1172. "auth-platform": '1', //请求平台ID 1:web端 2:安卓 3:苹果
  1173. "auth-appid": uni.getStorageSync('user_info').app_id || 0, //通过二维码进入携带的appid
  1174. "auth-orgname": uni.getStorageSync('user_info').org_info.orgname || orgname || '', //用户机构
  1175. },
  1176. timeout: this.config.timeout,
  1177. success: (res) => {
  1178. let pages = getCurrentPages();
  1179. if (res.data.result.status_info.status_code != 100) {
  1180. let pagesitem = pages[2] || pages[1] || pages[0]
  1181. if (pagesitem.route == "PackageA/pages/vote_work/video_upload") {
  1182. pagesitem.$vm.$refs.sbp.close()
  1183. pagesitem.$vm.isupload = false
  1184. pagesitem.$vm.showMsk = false
  1185. }
  1186. uni.showToast({
  1187. title: res.data.result.status_info.status_message,
  1188. duration: 2000,
  1189. icon: 'none'
  1190. })
  1191. this.cancel();
  1192. return
  1193. }
  1194. const statusCode = res.statusCode;
  1195. // 标示成功的返回码
  1196. if (successStatus.includes(statusCode)) {
  1197. resolve(res);
  1198. // 标示失败的返回码
  1199. } else if (failStatus.includes(statusCode)) {
  1200. reject(res);
  1201. } else if (retries > 0) {
  1202. setTimeout(() => {
  1203. this.emit('retry', {
  1204. statusCode,
  1205. url: args.url
  1206. });
  1207. --retries;
  1208. doRequest();
  1209. }, chunkRetryInterval);
  1210. } else {
  1211. reject(res);
  1212. }
  1213. },
  1214. fail: (res) => {
  1215. reject(res);
  1216. let pageS = getCurrentPages();
  1217. let pagesitem = pageS[2] || pageS[1] || pageS[0]
  1218. if (pagesitem.route == "PackageA/pages/vote_work/video_upload") {
  1219. console.log(pagesitem, 'pagesitem----');
  1220. pagesitem.$vm.$refs.sbp.close()
  1221. // pagesitem.$vm.imgvido_arr = []
  1222. pagesitem.$vm.isupload = false
  1223. pagesitem.$vm.showMsk = false
  1224. }
  1225. uni.showToast({
  1226. title: res.data.result.status_info.status_message,
  1227. duration: 2000,
  1228. icon: 'none'
  1229. })
  1230. console.log(res, '请求失败');
  1231. return
  1232. }
  1233. });
  1234. if (isFunction(callback)) {
  1235. callback(task);
  1236. }
  1237. };
  1238. doRequest();
  1239. })
  1240. }
  1241. handleFail(e) {
  1242. if (this.isFail) return
  1243. logger.error('upload file fail: ', e);
  1244. this.isFail = true;
  1245. this.cancel();
  1246. this.emit('fail', e);
  1247. this.emit('complete', e);
  1248. }
  1249. _event() { // step4: 发送合并请求
  1250. // step4: 发送合并请求
  1251. this.on('uploadDone', async () => {
  1252. logger.timeEnd('[Uploader] uploadChunks');
  1253. logger.info('upload chunks end');
  1254. this.isUploading = false;
  1255. logger.info('start merge reqeust');
  1256. logger.time('[Uploader] mergeRequest');
  1257. const [mergeErr, mergeResp] = await awaitWrap(this.mergeRequest());
  1258. logger.timeEnd('[Uploader] mergeRequest');
  1259. logger.info('merge reqeust end');
  1260. logger.debug('mergeRequest', mergeErr, mergeResp);
  1261. if (mergeErr) {
  1262. this.handleFail({
  1263. errCode: 20003,
  1264. errrMsg: mergeErr.errMsg
  1265. });
  1266. return
  1267. }
  1268. logger.info('upload file success');
  1269. this.emit('success', {
  1270. errCode: 0,
  1271. ...mergeResp.data
  1272. });
  1273. this.emit('complete', {
  1274. errCode: 0,
  1275. ...mergeResp.data
  1276. });
  1277. });
  1278. }
  1279. _upload() {
  1280. this.startUploadTime = Date.now();
  1281. this._uploadedSize = 0;
  1282. if (this.chunksQueue.length) {
  1283. const maxConcurrency = this.config.maxConcurrency;
  1284. for (let i = 0; i < maxConcurrency; i++) {
  1285. this.uploadChunk();
  1286. }
  1287. } else {
  1288. this.readFileChunk();
  1289. }
  1290. }
  1291. updateUploadSize(currUploadSize) {
  1292. this.uploadedSize += currUploadSize; // 总体上传大小,暂停后累计
  1293. this._uploadedSize += currUploadSize; // 上传大小,暂停后清空
  1294. const time = Date.now() - this.startUploadTime; // 当前耗时
  1295. const averageSpeed = this._uploadedSize / time; // B/ms
  1296. const sizeWaitSend = this.sizeNeedSend - this.uploadedSize; // 剩余需要发送的大小
  1297. this.timeRemaining = parseInt(sizeWaitSend / averageSpeed, 10); // 剩余时间
  1298. this.averageSpeed = parseInt(averageSpeed, 10) * 1000; // 平均速度 B/s
  1299. this.progress = parseInt(((this.uploadedSize * 100) / this.sizeNeedSend), 10);
  1300. this.dispatchProgress();
  1301. }
  1302. dispatchProgress() {
  1303. this.emit('progress', {
  1304. totalSize: this.totalSize,
  1305. progress: this.progress,
  1306. uploadedSize: this.uploadedSize,
  1307. averageSpeed: this.averageSpeed,
  1308. timeRemaining: this.timeRemaining
  1309. });
  1310. }
  1311. pause() {
  1312. logger.info('** pause **');
  1313. this.isUploading = false;
  1314. const abortIndex = Object.keys(this.uploadTasks).map(v => v * 1);
  1315. abortIndex.forEach(index => {
  1316. this.chunksIndexNeedRead.push(index);
  1317. this.uploadTasks[index].abort();
  1318. });
  1319. this.uploadTasks = {};
  1320. }
  1321. resume() {
  1322. logger.info('** resume **');
  1323. this.isUploading = true;
  1324. this._upload();
  1325. }
  1326. cancel() {
  1327. logger.info('** cancel **');
  1328. this.pause();
  1329. this._reset();
  1330. }
  1331. _reset() {
  1332. this.chunksIndexNeedRead = Array.from(Array(this.totalChunks).keys());
  1333. this.chunksIndexNeedSend = Array.from(Array(this.totalChunks).keys());
  1334. this.chunksNeedSend = this.totalChunks;
  1335. this.sizeNeedSend = this.totalSize;
  1336. this.identifier = '';
  1337. this.chunksSend = 0;
  1338. this.chunksQueue = [];
  1339. this.uploadTasks = {};
  1340. this.pUploadList = [];
  1341. this.uploadedChunks = [];
  1342. this.isUploading = false;
  1343. this.isFail = false;
  1344. this.progress = 0;
  1345. this.uploadedSize = 0;
  1346. this.averageSpeed = 0;
  1347. this.timeRemaining = Number.POSITIVE_INFINITY;
  1348. this.dispatchProgress();
  1349. }
  1350. readFileChunk() {
  1351. const {
  1352. tempFilePath,
  1353. chunkSize,
  1354. maxLoadChunks,
  1355. chunksQueue,
  1356. chunksIndexNeedRead,
  1357. totalSize
  1358. } = this;
  1359. const chunks = Math.min(chunksIndexNeedRead.length, maxLoadChunks - chunksQueue.length);
  1360. // 异步读取
  1361. logger.debug(`readFileChunk chunks: ${chunks}, chunksIndexNeedRead`, this.chunksIndexNeedRead);
  1362. for (let i = 0; i < chunks; i++) {
  1363. const index = chunksIndexNeedRead.shift();
  1364. const position = index * chunkSize;
  1365. const length = Math.min(totalSize - position, chunkSize);
  1366. if (this.isFail) break
  1367. readFileAsync({
  1368. filePath: tempFilePath,
  1369. position,
  1370. length
  1371. }).then(res => {
  1372. const chunk = res.data;
  1373. this.chunksQueue.push({
  1374. chunk,
  1375. length,
  1376. index
  1377. });
  1378. this.uploadChunk();
  1379. return null
  1380. }).catch(e => {
  1381. this.handleFail({
  1382. errCode: 10001,
  1383. errMsg: e.errMsg
  1384. });
  1385. });
  1386. }
  1387. }
  1388. uploadChunk() { //暂停 / 继续 /取消
  1389. // 暂停中
  1390. if (!this.isUploading || this.isFail) return
  1391. // 没有更多数据了
  1392. if (!this.chunksQueue.length) return
  1393. // 达到最大并发度
  1394. if (Object.keys(this.uploadTasks).length === this.config.maxConcurrency) return
  1395. const {
  1396. chunk,
  1397. index,
  1398. length
  1399. } = this.chunksQueue.shift();
  1400. // 跳过已发送的分块
  1401. if (this.uploadedChunks.includes(index)) {
  1402. this.uploadChunk();
  1403. return
  1404. }
  1405. const {
  1406. uploadUrl,
  1407. query,
  1408. header
  1409. } = this.config;
  1410. const identifier = this.identifier;
  1411. const url = addParams(uploadUrl, {
  1412. ...query,
  1413. identifier,
  1414. index,
  1415. chunkSize: length,
  1416. fileName: this.config.fileName,
  1417. totalChunks: this.totalChunks,
  1418. totalSize: this.totalSize
  1419. });
  1420. logger.debug(`uploadChunk index: ${index}, lenght ${length}`);
  1421. logger.time(`[Uploader] uploadChunk index-${index}`);
  1422. this._requestAsync({
  1423. url,
  1424. data: chunk,
  1425. header: {
  1426. ...header,
  1427. 'content-type': 'application/octet-stream'
  1428. },
  1429. method: 'POST',
  1430. }, (task) => {
  1431. this.uploadTasks[index] = task;
  1432. }).then((res) => {
  1433. if (res.data.result.status_info.status_code != 100) {
  1434. this.cancel();
  1435. return
  1436. }
  1437. this.chunksSend++;
  1438. delete this.uploadTasks[index];
  1439. this.updateUploadSize(length);
  1440. logger.debug(`uploadChunk success chunksSend: ${this.chunksSend}`);
  1441. logger.timeEnd(`[Uploader] uploadChunk index-${index}`);
  1442. // 尝试继续加载文件
  1443. this.readFileChunk();
  1444. // 尝试继续发送下一条
  1445. this.uploadChunk();
  1446. // 所有分片发送完毕
  1447. if (this.chunksSend === this.chunksNeedSend) {
  1448. this.emit('uploadDone');
  1449. }
  1450. return null
  1451. }).catch(res => {
  1452. if (res.errMsg.includes('request:fail abort')) {
  1453. logger.info(`chunk index-${index} will be aborted`);
  1454. } else {
  1455. this.handleFail({
  1456. errCode: 20002,
  1457. errMsg: res.errMsg
  1458. });
  1459. }
  1460. });
  1461. }
  1462. emit(event, data) {
  1463. this.emitter.emit(event, data);
  1464. }
  1465. on(event, listenr) {
  1466. this.emitter.on(event, listenr);
  1467. }
  1468. off(event, listenr) {
  1469. this.emitter.off(event, listenr);
  1470. }
  1471. generateIdentifier() {
  1472. let identifier = '';
  1473. const generator = this.config.generateIdentifier;
  1474. if (isFunction(generator)) {
  1475. identifier = generator();
  1476. } else {
  1477. const uuid = `${appId}-${Date.now()}-${Math.random()}`;
  1478. identifier = sparkMd5.hash(uuid);
  1479. }
  1480. return identifier
  1481. }
  1482. async computeMD5() {
  1483. const {
  1484. tempFilePath,
  1485. totalSize,
  1486. chunkSize
  1487. } = this;
  1488. // 文件比内存限制小时,保存分片
  1489. const isltMaxMemory = totalSize < this.config.maxMemory;
  1490. const sliceSize = isltMaxMemory ? chunkSize : 10 * MB;
  1491. const sliceNum = Math.ceil(totalSize / sliceSize);
  1492. const spark = new sparkMd5.ArrayBuffer();
  1493. for (let i = 0; i < sliceNum; i++) {
  1494. const position = i * sliceSize;
  1495. const length = Math.min(totalSize - position, sliceSize);
  1496. // eslint-disable-next-line no-await-in-loop
  1497. const [readFileErr, readFileResp] = await awaitWrap(readFileAsync({
  1498. filePath: tempFilePath,
  1499. position,
  1500. length
  1501. }));
  1502. if (readFileErr) {
  1503. spark.destroy();
  1504. throw (new Error(readFileErr.errMsg))
  1505. }
  1506. const chunk = readFileResp.data;
  1507. if (isltMaxMemory) {
  1508. this.chunksQueue.push({
  1509. chunk,
  1510. length,
  1511. index: i
  1512. });
  1513. }
  1514. spark.append(chunk);
  1515. }
  1516. this.chunksIndexNeedRead = [];
  1517. const identifier = spark.end();
  1518. spark.destroy();
  1519. return identifier
  1520. }
  1521. async verifyRequest() {
  1522. const {
  1523. verifyUrl,
  1524. fileName
  1525. } = this.config;
  1526. const verifyResp = await this._requestAsync({
  1527. url: verifyUrl,
  1528. data: {
  1529. fileName,
  1530. identifier: this.identifier
  1531. }
  1532. });
  1533. return verifyResp
  1534. }
  1535. async mergeRequest() {
  1536. const {
  1537. mergeUrl,
  1538. fileName
  1539. } = this.config;
  1540. const mergeResp = await this._requestAsync({
  1541. url: mergeUrl,
  1542. data: {
  1543. fileName,
  1544. identifier: this.identifier
  1545. }
  1546. });
  1547. return mergeResp
  1548. }
  1549. }
  1550. export default Uploader;
  1551. //# sourceMappingURL=uploader.js.map

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/66671
推荐阅读
相关标签
  

闽ICP备14008679号