一、安装
npm i miniprogram-file-uploader
二、页面引入
import Uploader from 'miniprogram-file-uploader'
三、实现功能(重要)
1.获取图片的路径
2.设置分片的大小
3.将数据放入
let obj = this.uploadItem //图片路径或者视频路径 可以通过chooseMedia的api进行获取
var tempFilePath = obj.tempFilePath
var file = {//重点,分片要的参数
ext_file_name: '',
index: 0,
chunkSize: 1024 * 1024 * 0.5 //分片0.5M 根据自己的分片需求设置一片多大
}
file.ext_file_name = obj.tempFilePath
file.index = Math.ceil(obj.size / file.chunkSize) //获取索引
var opt = {
fileName: file.ext_file_name,
totalSize: obj.size,
chunkSize: file.chunkSize,
query: { //后端需要的参数
activity_id: this.activity_id,
file_type: this.file_type,
total_size: obj.size,
ext_file_name: file.ext_file_name
},
timeout: 180000, //请求超时时间,默认 10000 ms
verifyUrl: 'https://xxxxxxx/upload/b2b3.0/digital_activity/?act=verify',//后端提供接口路径
uploadUrl: 'https://xxxxxxx/upload/b2b3.0/digital_activity/?act=upload',//后端提供接口路径
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}`, //合并分块 //后端提供接口路径 参数自己传
tempFilePath: tempFilePath //图片路径
}
const uploader = new Uploader(opt)//在这里扭一下
四、实现上传进度条
<view class="prompt_box" >
<view class="prompt_top">
<view class="prompt_title two_ti">正在提交</view>
<view class="prompt_tow">
<view class="">
<progress :percent="upload.progress" stroke-width="4px" class="progress"
:activeColor='theme_config.theme_color' show-info="true" font-size="15px"
border-radius="4px"></progress>
</view>
<view class="">正在上传,请耐心等待</view>
</view>
</view>
<view class="prompt_but two" @click="CancelSubmission">
<view>取消提交</view>
</view>
</view>
data(){
return{
upload: { //上传进度
// eq: -1,
size: 0,
progress: 0,
uploadedSize: 0,
averageSpeed: 0,
timeRemaining: 0
},
}
}
//js------
const uploader = new Uploader(opt)//在这里扭一下
uploader.on('complete', (res) => {})
uploader.on('retry', (res) => {})
uploader.on('success', (res) => {
if (res.result.status_info.status_code == 100) {
this.PostSubmitWork(res.result.build_info)
}
})
uploader.on('fail', (res) => {
this.isupload = false
})
uploader.on('progress', (res) => { //上传进度这里是进度条提供
var tmp = {
size: obj.size / 1024,
progress: res.progress,
uploadedSize: parseInt(res.uploadedSize / 1024),
averageSpeed: parseInt(res.averageSpeed / 1024),
timeRemaining: res.timeRemaining / 1000
}
this.upload = tmp//进度的所有数据
})
全部函数js
// 上传逻辑
uploadOne() {
let obj = this.uploadItem //图片路径或者视频路径 可以通过chooseMedia的api进行获取
var tempFilePath = obj.tempFilePath
var file = {//重点,分片要的参数
ext_file_name: '',
index: 0,
chunkSize: 1024 * 1024 * 0.5 //分片0.5M 根据自己的分片需求设置一片多大
}
file.ext_file_name = obj.tempFilePath
file.index = Math.ceil(obj.size / file.chunkSize) //获取索引
var opt = {
fileName: file.ext_file_name,
totalSize: obj.size,
chunkSize: file.chunkSize,
query: { //后端需要的参数
activity_id: this.activity_id,
file_type: this.file_type,
total_size: obj.size,
ext_file_name: file.ext_file_name
},
timeout: 180000, //请求超时时间,默认 10000 ms
verifyUrl: 'https://xxxxxxx/upload/b2b3.0/digital_activity/?act=verify',//后端提供接口路径
uploadUrl: 'https://xxxxxxx/upload/b2b3.0/digital_activity/?act=upload',//后端提供接口路径
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}`, //合并分块 //后端提供接口路径 参数自己传
tempFilePath: tempFilePath //图片路径
}
const uploader = new Uploader(opt)//在这里扭一下
// 成功或失败都会触发
uploader.on('complete', (res) => {})
uploader.on('retry', (res) => {})
uploader.on('success', (res) => {
if (res.result.status_info.status_code == 100) {
this.PostSubmitWork(res.result.build_info)
}
})
uploader.on('fail', (res) => {
this.isupload = false
})
uploader.on('progress', (res) => { //上传进度这里是进度条提供
var tmp = {
size: obj.size / 1024,
progress: res.progress,
uploadedSize: parseInt(res.uploadedSize / 1024),
averageSpeed: parseInt(res.averageSpeed / 1024),
timeRemaining: res.timeRemaining / 1000
}
this.upload = tmp
})
uploader.upload()
this.uploader = uploader
},
五、修改npm的源码,处理请求源码中请求所携带的参数问题,以及报错处理(重要)
修改npm后的源码。可直接用(需修改请求参数)
/**
* miniprogram-uploader 1.0.0
* description: A JavaScript library supports miniprogram to upload large file.
* author: sanfordsun
* Released under the MIT License.
*/
import md5 from 'js-md5'
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window :
typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, basedir, module) {
return module = {
path: basedir,
exports: {},
require: function(path, base) {
return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
}
}, fn(module, module.exports), module.exports;
}
function commonjsRequire() {
throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
}
var logger = createCommonjsModule(function(module) {
/*!
* js-logger - http://github.com/jonnyreeves/js-logger
* Jonny Reeves, http://jonnyreeves.co.uk/
* js-logger may be freely distributed under the MIT license.
*/
(function(global) {
// Top level module for the global, static logger instance.
var Logger = {};
// For those that are at home that are keeping score.
Logger.VERSION = "1.6.0";
// Function which handles all incoming log messages.
var logHandler;
// Map of ContextualLogger instances by name; used by Logger.get() to return the same named instance.
var contextualLoggersByNameMap = {};
// Polyfill for ES5's Function.bind.
var bind = function(scope, func) {
return function() {
return func.apply(scope, arguments);
};
};
// Super exciting object merger-matron 9000 adding another 100 bytes to your download.
var merge = function() {
var args = arguments,
target = args[0],
key, i;
for (i = 1; i < args.length; i++) {
for (key in args[i]) {
if (!(key in target) && args[i].hasOwnProperty(key)) {
target[key] = args[i][key];
}
}
}
return target;
};
// Helper to define a logging level object; helps with optimisation.
var defineLogLevel = function(value, name) {
return {
value: value,
name: name
};
};
// Predefined logging levels.
Logger.TRACE = defineLogLevel(1, 'TRACE');
Logger.DEBUG = defineLogLevel(2, 'DEBUG');
Logger.INFO = defineLogLevel(3, 'INFO');
Logger.TIME = defineLogLevel(4, 'TIME');
Logger.WARN = defineLogLevel(5, 'WARN');
Logger.ERROR = defineLogLevel(8, 'ERROR');
Logger.OFF = defineLogLevel(99, 'OFF');
// Inner class which performs the bulk of the work; ContextualLogger instances can be configured independently
// of each other.
var ContextualLogger = function(defaultContext) {
this.context = defaultContext;
this.setLevel(defaultContext.filterLevel);
this.log = this.info; // Convenience alias.
};
ContextualLogger.prototype = {
// Changes the current logging level for the logging instance.
setLevel: function(newLevel) {
// Ensure the supplied Level object looks valid.
if (newLevel && "value" in newLevel) {
this.context.filterLevel = newLevel;
}
},
// Gets the current logging level for the logging instance
getLevel: function() {
return this.context.filterLevel;
},
// Is the logger configured to output messages at the supplied level?
enabledFor: function(lvl) {
var filterLevel = this.context.filterLevel;
return lvl.value >= filterLevel.value;
},
trace: function() {
this.invoke(Logger.TRACE, arguments);
},
debug: function() {
this.invoke(Logger.DEBUG, arguments);
},
info: function() {
this.invoke(Logger.INFO, arguments);
},
warn: function() {
this.invoke(Logger.WARN, arguments);
},
error: function() {
this.invoke(Logger.ERROR, arguments);
},
time: function(label) {
if (typeof label === 'string' && label.length > 0) {
this.invoke(Logger.TIME, [label, 'start']);
}
},
timeEnd: function(label) {
if (typeof label === 'string' && label.length > 0) {
this.invoke(Logger.TIME, [label, 'end']);
}
},
// Invokes the logger callback if it's not being filtered.
invoke: function(level, msgArgs) {
if (logHandler && this.enabledFor(level)) {
logHandler(msgArgs, merge({
level: level
}, this.context));
}
}
};
// Protected instance which all calls to the to level `Logger` module will be routed through.
var globalLogger = new ContextualLogger({
filterLevel: Logger.OFF
});
// Configure the global Logger instance.
(function() {
// Shortcut for optimisers.
var L = Logger;
L.enabledFor = bind(globalLogger, globalLogger.enabledFor);
L.trace = bind(globalLogger, globalLogger.trace);
L.debug = bind(globalLogger, globalLogger.debug);
L.time = bind(globalLogger, globalLogger.time);
L.timeEnd = bind(globalLogger, globalLogger.timeEnd);
L.info = bind(globalLogger, globalLogger.info);
L.warn = bind(globalLogger, globalLogger.warn);
L.error = bind(globalLogger, globalLogger.error);
// Don't forget the convenience alias!
L.log = L.info;
}());
// Set the global logging handler. The supplied function should expect two arguments, the first being an arguments
// object with the supplied log messages and the second being a context object which contains a hash of stateful
// parameters which the logging function can consume.
Logger.setHandler = function(func) {
logHandler = func;
};
// Sets the global logging filter level which applies to *all* previously registered, and future Logger instances.
// (note that named loggers (retrieved via `Logger.get`) can be configured independently if required).
Logger.setLevel = function(level) {
// Set the globalLogger's level.
globalLogger.setLevel(level);
// Apply this level to all registered contextual loggers.
for (var key in contextualLoggersByNameMap) {
if (contextualLoggersByNameMap.hasOwnProperty(key)) {
contextualLoggersByNameMap[key].setLevel(level);
}
}
};
// Gets the global logging filter level
Logger.getLevel = function() {
return globalLogger.getLevel();
};
// Retrieve a ContextualLogger instance. Note that named loggers automatically inherit the global logger's level,
// default context and log handler.
Logger.get = function(name) {
// All logger instances are cached so they can be configured ahead of use.
return contextualLoggersByNameMap[name] ||
(contextualLoggersByNameMap[name] = new ContextualLogger(merge({
name: name
}, globalLogger.context)));
};
// CreateDefaultHandler returns a handler function which can be passed to `Logger.setHandler()` which will
// write to the window's console object (if present); the optional options object can be used to customise the
// formatter used to format each log message.
Logger.createDefaultHandler = function(options) {
options = options || {};
options.formatter = options.formatter || function defaultMessageFormatter(messages, context) {
// Prepend the logger's name to the log message for easy identification.
if (context.name) {
messages.unshift("[" + context.name + "]");
}
};
// Map of timestamps by timer labels used to track `#time` and `#timeEnd()` invocations in environments
// that don't offer a native console method.
var timerStartTimeByLabelMap = {};
// Support for IE8+ (and other, slightly more sane environments)
var invokeConsoleMethod = function(hdlr, messages) {
Function.prototype.apply.call(hdlr, console, messages);
};
// Check for the presence of a logger.
if (typeof console === "undefined") {
return function() {
/* no console */
};
}
return function(messages, context) {
// Convert arguments object to Array.
messages = Array.prototype.slice.call(messages);
var hdlr = console.log;
var timerLabel;
if (context.level === Logger.TIME) {
timerLabel = (context.name ? '[' + context.name + '] ' : '') + messages[0];
if (messages[1] === 'start') {
if (console.time) {
console.time(timerLabel);
} else {
timerStartTimeByLabelMap[timerLabel] = new Date().getTime();
}
} else {
if (console.timeEnd) {
console.timeEnd(timerLabel);
} else {
invokeConsoleMethod(hdlr, [timerLabel + ': ' +
(new Date().getTime() - timerStartTimeByLabelMap[timerLabel]) + 'ms'
]);
}
}
} else {
// Delegate through to custom warn/error loggers if present on the console.
if (context.level === Logger.WARN && console.warn) {
hdlr = console.warn;
} else if (context.level === Logger.ERROR && console.error) {
hdlr = console.error;
} else if (context.level === Logger.INFO && console.info) {
hdlr = console.info;
} else if (context.level === Logger.DEBUG && console.debug) {
hdlr = console.debug;
} else if (context.level === Logger.TRACE && console.trace) {
hdlr = console.trace;
}
options.formatter(messages, context);
invokeConsoleMethod(hdlr, messages);
}
};
};
// Configure and example a Default implementation which writes to the `window.console` (if present). The
// `options` hash can be used to configure the default logLevel and provide a custom message formatter.
Logger.useDefaults = function(options) {
Logger.setLevel(options && options.defaultLevel || Logger.DEBUG);
Logger.setHandler(Logger.createDefaultHandler(options));
};
// Export to popular environments boilerplate.
if (module.exports) {
module.exports = Logger;
} else {
Logger._prevLogger = global.Logger;
Logger.noConflict = function() {
global.Logger = Logger._prevLogger;
return Logger;
};
global.Logger = Logger;
}
}(commonjsGlobal));
});
var sparkMd5 = createCommonjsModule(function(module, exports) {
(function(factory) {
{
// Node/CommonJS
module.exports = factory();
}
}(function(undefined$1) {
/*
* Fastest md5 implementation around (JKM md5).
* Credits: Joseph Myers
*
* @see http://www.myersdaily.org/joseph/javascript/md5-text.html
* @see http://jsperf.com/md5-shootout/7
*/
/* this function is much faster,
so if possible we use it. Some IEs
are the only ones I know of that
need the idiotic second function,
generated by an if clause. */
var hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
function md5cycle(x, k) {
var a = x[0],
b = x[1],
c = x[2],
d = x[3];
a += (b & c | ~b & d) + k[0] - 680876936 | 0;
a = (a << 7 | a >>> 25) + b | 0;
d += (a & b | ~a & c) + k[1] - 389564586 | 0;
d = (d << 12 | d >>> 20) + a | 0;
c += (d & a | ~d & b) + k[2] + 606105819 | 0;
c = (c << 17 | c >>> 15) + d | 0;
b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
b = (b << 22 | b >>> 10) + c | 0;
a += (b & c | ~b & d) + k[4] - 176418897 | 0;
a = (a << 7 | a >>> 25) + b | 0;
d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
d = (d << 12 | d >>> 20) + a | 0;
c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
c = (c << 17 | c >>> 15) + d | 0;
b += (c & d | ~c & a) + k[7] - 45705983 | 0;
b = (b << 22 | b >>> 10) + c | 0;
a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
a = (a << 7 | a >>> 25) + b | 0;
d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
d = (d << 12 | d >>> 20) + a | 0;
c += (d & a | ~d & b) + k[10] - 42063 | 0;
c = (c << 17 | c >>> 15) + d | 0;
b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
b = (b << 22 | b >>> 10) + c | 0;
a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
a = (a << 7 | a >>> 25) + b | 0;
d += (a & b | ~a & c) + k[13] - 40341101 | 0;
d = (d << 12 | d >>> 20) + a | 0;
c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
c = (c << 17 | c >>> 15) + d | 0;
b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
b = (b << 22 | b >>> 10) + c | 0;
a += (b & d | c & ~d) + k[1] - 165796510 | 0;
a = (a << 5 | a >>> 27) + b | 0;
d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
d = (d << 9 | d >>> 23) + a | 0;
c += (d & b | a & ~b) + k[11] + 643717713 | 0;
c = (c << 14 | c >>> 18) + d | 0;
b += (c & a | d & ~a) + k[0] - 373897302 | 0;
b = (b << 20 | b >>> 12) + c | 0;
a += (b & d | c & ~d) + k[5] - 701558691 | 0;
a = (a << 5 | a >>> 27) + b | 0;
d += (a & c | b & ~c) + k[10] + 38016083 | 0;
d = (d << 9 | d >>> 23) + a | 0;
c += (d & b | a & ~b) + k[15] - 660478335 | 0;
c = (c << 14 | c >>> 18) + d | 0;
b += (c & a | d & ~a) + k[4] - 405537848 | 0;
b = (b << 20 | b >>> 12) + c | 0;
a += (b & d | c & ~d) + k[9] + 568446438 | 0;
a = (a << 5 | a >>> 27) + b | 0;
d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
d = (d << 9 | d >>> 23) + a | 0;
c += (d & b | a & ~b) + k[3] - 187363961 | 0;
c = (c << 14 | c >>> 18) + d | 0;
b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
b = (b << 20 | b >>> 12) + c | 0;
a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
a = (a << 5 | a >>> 27) + b | 0;
d += (a & c | b & ~c) + k[2] - 51403784 | 0;
d = (d << 9 | d >>> 23) + a | 0;
c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
c = (c << 14 | c >>> 18) + d | 0;
b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
b = (b << 20 | b >>> 12) + c | 0;
a += (b ^ c ^ d) + k[5] - 378558 | 0;
a = (a << 4 | a >>> 28) + b | 0;
d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
d = (d << 11 | d >>> 21) + a | 0;
c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
c = (c << 16 | c >>> 16) + d | 0;
b += (c ^ d ^ a) + k[14] - 35309556 | 0;
b = (b << 23 | b >>> 9) + c | 0;
a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
a = (a << 4 | a >>> 28) + b | 0;
d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
d = (d << 11 | d >>> 21) + a | 0;
c += (d ^ a ^ b) + k[7] - 155497632 | 0;
c = (c << 16 | c >>> 16) + d | 0;
b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
b = (b << 23 | b >>> 9) + c | 0;
a += (b ^ c ^ d) + k[13] + 681279174 | 0;
a = (a << 4 | a >>> 28) + b | 0;
d += (a ^ b ^ c) + k[0] - 358537222 | 0;
d = (d << 11 | d >>> 21) + a | 0;
c += (d ^ a ^ b) + k[3] - 722521979 | 0;
c = (c << 16 | c >>> 16) + d | 0;
b += (c ^ d ^ a) + k[6] + 76029189 | 0;
b = (b << 23 | b >>> 9) + c | 0;
a += (b ^ c ^ d) + k[9] - 640364487 | 0;
a = (a << 4 | a >>> 28) + b | 0;
d += (a ^ b ^ c) + k[12] - 421815835 | 0;
d = (d << 11 | d >>> 21) + a | 0;
c += (d ^ a ^ b) + k[15] + 530742520 | 0;
c = (c << 16 | c >>> 16) + d | 0;
b += (c ^ d ^ a) + k[2] - 995338651 | 0;
b = (b << 23 | b >>> 9) + c | 0;
a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
a = (a << 6 | a >>> 26) + b | 0;
d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
d = (d << 10 | d >>> 22) + a | 0;
c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
c = (c << 15 | c >>> 17) + d | 0;
b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
b = (b << 21 | b >>> 11) + c | 0;
a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
a = (a << 6 | a >>> 26) + b | 0;
d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
d = (d << 10 | d >>> 22) + a | 0;
c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
c = (c << 15 | c >>> 17) + d | 0;
b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
b = (b << 21 | b >>> 11) + c | 0;
a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
a = (a << 6 | a >>> 26) + b | 0;
d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
d = (d << 10 | d >>> 22) + a | 0;
c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
c = (c << 15 | c >>> 17) + d | 0;
b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
b = (b << 21 | b >>> 11) + c | 0;
a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
a = (a << 6 | a >>> 26) + b | 0;
d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
d = (d << 10 | d >>> 22) + a | 0;
c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
c = (c << 15 | c >>> 17) + d | 0;
b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
b = (b << 21 | b >>> 11) + c | 0;
x[0] = a + x[0] | 0;
x[1] = b + x[1] | 0;
x[2] = c + x[2] | 0;
x[3] = d + x[3] | 0;
}
function md5blk(s) {
var md5blks = [],
i; /* Andy King said do it this way. */
for (i = 0; i < 64; i += 4) {
md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s
.charCodeAt(i + 3) << 24);
}
return md5blks;
}
function md5blk_array(a) {
var md5blks = [],
i; /* Andy King said do it this way. */
for (i = 0; i < 64; i += 4) {
md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
}
return md5blks;
}
function md51(s) {
var n = s.length,
state = [1732584193, -271733879, -1732584194, 271733878],
i,
length,
tail,
tmp,
lo,
hi;
for (i = 64; i <= n; i += 64) {
md5cycle(state, md5blk(s.substring(i - 64, i)));
}
s = s.substring(i - 64);
length = s.length;
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
}
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(state, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Beware that the final length might not fit in 32 bits so we take care of that
tmp = n * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(state, tail);
return state;
}
function md51_array(a) {
var n = a.length,
state = [1732584193, -271733879, -1732584194, 271733878],
i,
length,
tail,
tmp,
lo,
hi;
for (i = 64; i <= n; i += 64) {
md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
}
// Not sure if it is a bug, however IE10 will always produce a sub array of length 1
// containing the last element of the parent array if the sub array specified starts
// beyond the length of the parent array - weird.
// https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);
length = a.length;
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= a[i] << ((i % 4) << 3);
}
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(state, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Beware that the final length might not fit in 32 bits so we take care of that
tmp = n * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(state, tail);
return state;
}
function rhex(n) {
var s = '',
j;
for (j = 0; j < 4; j += 1) {
s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
}
return s;
}
function hex(x) {
var i;
for (i = 0; i < x.length; i += 1) {
x[i] = rhex(x[i]);
}
return x.join('');
}
// In some cases the fast add32 function cannot be used..
if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592');
// ---------------------------------------------------
/**
* ArrayBuffer slice polyfill.
*
* @see https://github.com/ttaubert/node-arraybuffer-slice
*/
if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
(function() {
function clamp(val, length) {
val = (val | 0) || 0;
if (val < 0) {
return Math.max(val + length, 0);
}
return Math.min(val, length);
}
ArrayBuffer.prototype.slice = function(from, to) {
var length = this.byteLength,
begin = clamp(from, length),
end = length,
num,
target,
targetArray,
sourceArray;
if (to !== undefined$1) {
end = clamp(to, length);
}
if (begin > end) {
return new ArrayBuffer(0);
}
num = end - begin;
target = new ArrayBuffer(num);
targetArray = new Uint8Array(target);
sourceArray = new Uint8Array(this, begin, num);
targetArray.set(sourceArray);
return target;
};
})();
}
// ---------------------------------------------------
/**
* Helpers.
*/
function toUtf8(str) {
if (/[\u0080-\uFFFF]/.test(str)) {
str = unescape(encodeURIComponent(str));
}
return str;
}
function utf8Str2ArrayBuffer(str, returnUInt8Array) {
var length = str.length,
buff = new ArrayBuffer(length),
arr = new Uint8Array(buff),
i;
for (i = 0; i < length; i += 1) {
arr[i] = str.charCodeAt(i);
}
return returnUInt8Array ? arr : buff;
}
function arrayBuffer2Utf8Str(buff) {
return String.fromCharCode.apply(null, new Uint8Array(buff));
}
function concatenateArrayBuffers(first, second, returnUInt8Array) {
var result = new Uint8Array(first.byteLength + second.byteLength);
result.set(new Uint8Array(first));
result.set(new Uint8Array(second), first.byteLength);
return returnUInt8Array ? result : result.buffer;
}
function hexToBinaryString(hex) {
var bytes = [],
length = hex.length,
x;
for (x = 0; x < length - 1; x += 2) {
bytes.push(parseInt(hex.substr(x, 2), 16));
}
return String.fromCharCode.apply(String, bytes);
}
// ---------------------------------------------------
/**
* SparkMD5 OOP implementation.
*
* Use this class to perform an incremental md5, otherwise use the
* static methods instead.
*/
function SparkMD5() {
// call reset to init the instance
this.reset();
}
/**
* Appends a string.
* A conversion will be applied if an utf8 string is detected.
*
* @param {String} str The string to be appended
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.append = function(str) {
// Converts the string to utf8 bytes if necessary
// Then append as binary
this.appendBinary(toUtf8(str));
return this;
};
/**
* Appends a binary string.
*
* @param {String} contents The binary string to be appended
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.appendBinary = function(contents) {
this._buff += contents;
this._length += contents.length;
var length = this._buff.length,
i;
for (i = 64; i <= length; i += 64) {
md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
}
this._buff = this._buff.substring(i - 64);
return this;
};
/**
* Finishes the incremental computation, reseting the internal state and
* returning the result.
*
* @param {Boolean} raw True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.prototype.end = function(raw) {
var buff = this._buff,
length = buff.length,
i,
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
ret;
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
}
this._finish(tail, length);
ret = hex(this._hash);
if (raw) {
ret = hexToBinaryString(ret);
}
this.reset();
return ret;
};
/**
* Resets the internal state of the computation.
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.reset = function() {
this._buff = '';
this._length = 0;
this._hash = [1732584193, -271733879, -1732584194, 271733878];
return this;
};
/**
* Gets the internal state of the computation.
*
* @return {Object} The state
*/
SparkMD5.prototype.getState = function() {
return {
buff: this._buff,
length: this._length,
hash: this._hash.slice()
};
};
/**
* Gets the internal state of the computation.
*
* @param {Object} state The state
*
* @return {SparkMD5} The instance itself
*/
SparkMD5.prototype.setState = function(state) {
this._buff = state.buff;
this._length = state.length;
this._hash = state.hash;
return this;
};
/**
* Releases memory used by the incremental buffer and other additional
* resources. If you plan to use the instance again, use reset instead.
*/
SparkMD5.prototype.destroy = function() {
delete this._hash;
delete this._buff;
delete this._length;
};
/**
* Finish the final calculation based on the tail.
*
* @param {Array} tail The tail (will be modified)
* @param {Number} length The length of the remaining buffer
*/
SparkMD5.prototype._finish = function(tail, length) {
var i = length,
tmp,
lo,
hi;
tail[i >> 2] |= 0x80 << ((i % 4) << 3);
if (i > 55) {
md5cycle(this._hash, tail);
for (i = 0; i < 16; i += 1) {
tail[i] = 0;
}
}
// Do the final computation based on the tail and length
// Beware that the final length may not fit in 32 bits so we take care of that
tmp = this._length * 8;
tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
lo = parseInt(tmp[2], 16);
hi = parseInt(tmp[1], 16) || 0;
tail[14] = lo;
tail[15] = hi;
md5cycle(this._hash, tail);
};
/**
* Performs the md5 hash on a string.
* A conversion will be applied if utf8 string is detected.
*
* @param {String} str The string
* @param {Boolean} [raw] True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.hash = function(str, raw) {
// Converts the string to utf8 bytes if necessary
// Then compute it using the binary function
return SparkMD5.hashBinary(toUtf8(str), raw);
};
/**
* Performs the md5 hash on a binary string.
*
* @param {String} content The binary string
* @param {Boolean} [raw] True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.hashBinary = function(content, raw) {
var hash = md51(content),
ret = hex(hash);
return raw ? hexToBinaryString(ret) : ret;
};
// ---------------------------------------------------
/**
* SparkMD5 OOP implementation for array buffers.
*
* Use this class to perform an incremental md5 ONLY for array buffers.
*/
SparkMD5.ArrayBuffer = function() {
// call reset to init the instance
this.reset();
};
/**
* Appends an array buffer.
*
* @param {ArrayBuffer} arr The array to be appended
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.append = function(arr) {
var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
length = buff.length,
i;
this._length += arr.byteLength;
for (i = 64; i <= length; i += 64) {
md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
}
this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
return this;
};
/**
* Finishes the incremental computation, reseting the internal state and
* returning the result.
*
* @param {Boolean} raw True to get the raw string, false to get the hex string
*
* @return {String} The result
*/
SparkMD5.ArrayBuffer.prototype.end = function(raw) {
var buff = this._buff,
length = buff.length,
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
i,
ret;
for (i = 0; i < length; i += 1) {
tail[i >> 2] |= buff[i] << ((i % 4) << 3);
}
this._finish(tail, length);
ret = hex(this._hash);
if (raw) {
ret = hexToBinaryString(ret);
}
this.reset();
return ret;
};
/**
* Resets the internal state of the computation.
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.reset = function() {
this._buff = new Uint8Array(0);
this._length = 0;
this._hash = [1732584193, -271733879, -1732584194, 271733878];
return this;
};
/**
* Gets the internal state of the computation.
*
* @return {Object} The state
*/
SparkMD5.ArrayBuffer.prototype.getState = function() {
var state = SparkMD5.prototype.getState.call(this);
// Convert buffer to a string
state.buff = arrayBuffer2Utf8Str(state.buff);
return state;
};
/**
* Gets the internal state of the computation.
*
* @param {Object} state The state
*
* @return {SparkMD5.ArrayBuffer} The instance itself
*/
SparkMD5.ArrayBuffer.prototype.setState = function(state) {
// Convert string to buffer
state.buff = utf8Str2ArrayBuffer(state.buff, true);
return SparkMD5.prototype.setState.call(this, state);
};
SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
/**
* Performs the md5 hash on an array buffer.
*
* @param {ArrayBuffer} arr The array buffer
* @param {Boolean} [raw] True to get the raw string, false to get the hex one
*
* @return {String} The result
*/
SparkMD5.ArrayBuffer.hash = function(arr, raw) {
var hash = md51_array(new Uint8Array(arr)),
ret = hex(hash);
return raw ? hexToBinaryString(ret) : ret;
};
return SparkMD5;
}));
});
var config = {
tempFilePath: '',
totalSize: 0,
fileName: '',
verifyUrl: '',
uploadUrl: '',
mergeUrl: '',
maxConcurrency: 5,
generateIdentifier: null,
chunkSize: 5 * 1024 * 1024,
maxMemory: 100 * 1024 * 1024,
query: '',
header: {},
testChunks: false,
chunkRetryInterval: 0,
maxChunkRetries: 0,
timeout: 10000,
successStatus: [200, 201, 202],
failStatus: [404, 415, 500, 501],
verbose: false
};
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (typeof this.events[event] !== 'object') {
this.events[event] = [];
}
this.events[event].push(listener);
return () => this.off(event, listener)
}
off(event, listener) {
if (typeof this.events[event] === 'object') {
const idx = this.events[event].indexOf(listener);
if (idx > -1) {
this.events[event].splice(idx, 1);
}
}
}
emit(event, ...args) {
if (typeof this.events[event] === 'object') {
this.events[event].forEach(listener => listener.apply(this, args));
}
}
once(event, listener) {
const remove = this.on(event, (...args) => {
remove();
listener.apply(this, args);
});
}
}
const isFunction = x => typeof x === 'function';
function promisify(func) {
if (!isFunction(func)) return func
return (args = {}) => new Promise((resolve, reject) => {
func(
Object.assign(args, {
success: resolve,
fail: reject
})
);
})
}
function addParams(url = '', params = {}) {
const parts = url.split('&');
const query = Object.keys(params).map(key => `${key}=${params[key]}`).join('&');
return query ? `${parts[0]}&${query}` : parts[0]
}
const awaitWrap = (promise) => promise
.then(data => [null, data])
.catch(err => [err, null]);
const compareVersion = (v1, v2) => {
v1 = v1.split('.');
v2 = v2.split('.');
const len = Math.max(v1.length, v2.length);
while (v1.length < len) {
v1.push('0');
}
while (v2.length < len) {
v2.push('0');
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i], 10);
const num2 = parseInt(v2[i], 10);
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
};
logger.useDefaults({
defaultLevel: logger.OFF,
formatter(messages) {
const now = new Date();
const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
messages.unshift(time);
messages.unshift('[Uploader]');
}
});
const fileManager = wx.getFileSystemManager();
const readFileAsync = promisify(fileManager.readFile);
const miniProgram = wx.getAccountInfoSync();
const systemInfo = wx.getSystemInfoSync();
const appId = miniProgram.appId;
const MB = 1024 * 1024;
// -----------------下面是请求的数据,可以删掉
let app_key = 'xxx';
let key = 'xxx';
// 随机32位数
function nonce() {
let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B',
'C', 'D', 'E'
];
let res = '';
for (let i = 0; i < 32; i++) {
let pos = Math.round(Math.random() * (arr.length - 1));
res += arr[pos]
}
return res;
}
function signfn(action, postParam) {
// 时间戳
let ts = new Date().getTime().toString();
let randomNum = nonce();
let act = app_key; //请求带的url
function getSign(params, app_key, key) {
if (typeof params == "string") {
return paramsStrSort(params);
} else if (typeof params == "object") {
let arr = [];
for (let i in params) {
arr.push((i + "=" + params[i]));
}
return paramsStrSort(arr.join(("&")))
}
}
function paramsStrSort(paramsStr) {
let url = paramsStr;
let arr = [];
let p = url.split("&");
for (let i in p) {
let temp_arr = p[i].split("=");
arr.push((temp_arr[0].toLowerCase() + "=" + temp_arr[1]));
}
let urlStr = arr.sort().join("&");
let newUrl = urlStr + '&key=' + key;
return md5(newUrl);
}
var params = {
act,
'ts': ts,
'nonce': randomNum,
app_key
};
// 转成字符串
let signstr = JSON.stringify(params);
let sign = getSign(params, app_key, key);
return {
sign,
ts,
randomNum,
app_key
};
}
// -----------------上面是请求的数据,可以删掉 结束
class Uploader {
constructor(option = {}) {
if (option.verbose) logger.setLevel(logger.INFO);
logger.debug('construct option ', option);
this.config = Object.assign(config, option);
this.emitter = new EventEmitter();
this.totalSize = this.config.totalSize;
this.chunkSize = this.config.chunkSize;
this.tempFilePath = this.config.tempFilePath;
this.totalChunks = Math.ceil(this.totalSize / this.chunkSize);
this.maxLoadChunks = Math.floor(this.config.maxMemory / this.chunkSize);
this._event();
}
static isSupport() {
const version = systemInfo.SDKVersion;
return compareVersion(version, '2.10.0') >= 0
}
async upload() {
this._reset();
logger.info('start generateIdentifier');
// step1: 计算 identifier
try {
logger.time('[Uploader] generateIdentifier');
if (this.config.testChunks) {
this.identifier = await this.computeMD5();
} else {
this.identifier = this.generateIdentifier();
}
logger.timeEnd('[Uploader] generateIdentifier');
logger.debug('generateIdentifier ', this.identifier);
} catch (error) {
this.handleFail({
errCode: 10002,
errMsg: error.message
});
return
}
logger.info('generateIdentifier end');
// step2: 获取已上传分片
if (this.config.testChunks && this.config.verifyUrl) {
logger.info('start verify uploaded chunks');
logger.time('[Uploader] verifyRequest');
const [verifyErr, verifyResp] = await awaitWrap(this.verifyRequest());
logger.timeEnd('[Uploader] verifyRequest');
logger.debug('verifyRequest', verifyErr, verifyResp);
if (verifyErr) {
this.handleFail({
errCode: 20001,
errMsg: verifyErr.errMsg
});
return
}
const {
needUpload,
uploadedChunks,
} = verifyResp.data;
logger.info('verify uploaded chunks end');
// 秒传逻辑
// 找不到合成的文件
if (!needUpload) {
this.progress = 100;
this.timeRemaining = 0;
this.dispatchProgress();
this.emit('success', {
errCode: 0,
...verifyResp.data
});
this.emit('complete', {
errCode: 0,
...verifyResp.data
});
return
// 分片齐全,但没有合并
} else if (uploadedChunks.length === this.totalChunks) {
this.progress = 100;
this.timeRemaining = 0;
this.dispatchProgress();
this.emit('uploadDone');
return
} else {
this.chunksIndexNeedRead = this.chunksIndexNeedRead.filter(v => !uploadedChunks.includes(v));
this.chunksIndexNeedSend = this.chunksIndexNeedSend.filter(v => !uploadedChunks.includes(v));
this.uploadedChunks = uploadedChunks.sort();
}
}
this.chunksNeedSend = this.chunksIndexNeedSend.length;
this.sizeNeedSend = this.chunksNeedSend * this.chunkSize;
if (this.chunksIndexNeedSend.includes(this.totalChunks - 1)) {
this.sizeNeedSend -= (this.totalChunks * this.chunkSize - this.totalSize);
}
logger.debug(`
start upload
uploadedChunks: ${this.uploadedChunks},
chunksQueue: ${this.chunksQueue},
chunksIndexNeedRead: ${this.chunksIndexNeedRead},
chunksNeedSend: ${this.chunksIndexNeedSend},
sizeNeedSend: ${this.sizeNeedSend}
`);
logger.info('start upload chunks');
logger.time('[Uploader] uploadChunks');
// step3: 开始上传
this.isUploading = true;
this._upload();
}
_requestAsync(args = {}, callback) {
const {
chunkRetryInterval,
maxChunkRetries,
successStatus,
failStatus
} = this.config;
let retries = maxChunkRetries;
// args.url||
const signInfo = signfn(args.url)
return new Promise((resolve, reject) => {
let ts = new Date().getTime().toString();
const doRequest = () => {
const task = wx.request({
...args,
header: {
"Content-Type": "multipart/form-data",
"auth-ts": signInfo.ts, //时间戳
"auth-access-token": uni.getStorageSync('user_info').access_token || '',
"auth-nonce": signInfo.randomNum, //随机字符串(32位)
"auth-sign": signInfo.sign, //签名
"auth-key": signInfo.app_key, //应用的APP_KEY
"auth-platform": '1', //请求平台ID 1:web端 2:安卓 3:苹果
"auth-appid": uni.getStorageSync('user_info').app_id || 0, //通过二维码进入携带的appid
"auth-orgname": uni.getStorageSync('user_info').org_info.orgname || orgname || '', //用户机构
},
timeout: this.config.timeout,
success: (res) => {
let pages = getCurrentPages();
if (res.data.result.status_info.status_code != 100) {
let pagesitem = pages[2] || pages[1] || pages[0]
if (pagesitem.route == "PackageA/pages/vote_work/video_upload") {
pagesitem.$vm.$refs.sbp.close()
pagesitem.$vm.isupload = false
pagesitem.$vm.showMsk = false
}
uni.showToast({
title: res.data.result.status_info.status_message,
duration: 2000,
icon: 'none'
})
this.cancel();
return
}
const statusCode = res.statusCode;
// 标示成功的返回码
if (successStatus.includes(statusCode)) {
resolve(res);
// 标示失败的返回码
} else if (failStatus.includes(statusCode)) {
reject(res);
} else if (retries > 0) {
setTimeout(() => {
this.emit('retry', {
statusCode,
url: args.url
});
--retries;
doRequest();
}, chunkRetryInterval);
} else {
reject(res);
}
},
fail: (res) => {
reject(res);
let pageS = getCurrentPages();
let pagesitem = pageS[2] || pageS[1] || pageS[0]
if (pagesitem.route == "PackageA/pages/vote_work/video_upload") {
console.log(pagesitem, 'pagesitem----');
pagesitem.$vm.$refs.sbp.close()
// pagesitem.$vm.imgvido_arr = []
pagesitem.$vm.isupload = false
pagesitem.$vm.showMsk = false
}
uni.showToast({
title: res.data.result.status_info.status_message,
duration: 2000,
icon: 'none'
})
console.log(res, '请求失败');
return
}
});
if (isFunction(callback)) {
callback(task);
}
};
doRequest();
})
}
handleFail(e) {
if (this.isFail) return
logger.error('upload file fail: ', e);
this.isFail = true;
this.cancel();
this.emit('fail', e);
this.emit('complete', e);
}
_event() { // step4: 发送合并请求
// step4: 发送合并请求
this.on('uploadDone', async () => {
logger.timeEnd('[Uploader] uploadChunks');
logger.info('upload chunks end');
this.isUploading = false;
logger.info('start merge reqeust');
logger.time('[Uploader] mergeRequest');
const [mergeErr, mergeResp] = await awaitWrap(this.mergeRequest());
logger.timeEnd('[Uploader] mergeRequest');
logger.info('merge reqeust end');
logger.debug('mergeRequest', mergeErr, mergeResp);
if (mergeErr) {
this.handleFail({
errCode: 20003,
errrMsg: mergeErr.errMsg
});
return
}
logger.info('upload file success');
this.emit('success', {
errCode: 0,
...mergeResp.data
});
this.emit('complete', {
errCode: 0,
...mergeResp.data
});
});
}
_upload() {
this.startUploadTime = Date.now();
this._uploadedSize = 0;
if (this.chunksQueue.length) {
const maxConcurrency = this.config.maxConcurrency;
for (let i = 0; i < maxConcurrency; i++) {
this.uploadChunk();
}
} else {
this.readFileChunk();
}
}
updateUploadSize(currUploadSize) {
this.uploadedSize += currUploadSize; // 总体上传大小,暂停后累计
this._uploadedSize += currUploadSize; // 上传大小,暂停后清空
const time = Date.now() - this.startUploadTime; // 当前耗时
const averageSpeed = this._uploadedSize / time; // B/ms
const sizeWaitSend = this.sizeNeedSend - this.uploadedSize; // 剩余需要发送的大小
this.timeRemaining = parseInt(sizeWaitSend / averageSpeed, 10); // 剩余时间
this.averageSpeed = parseInt(averageSpeed, 10) * 1000; // 平均速度 B/s
this.progress = parseInt(((this.uploadedSize * 100) / this.sizeNeedSend), 10);
this.dispatchProgress();
}
dispatchProgress() {
this.emit('progress', {
totalSize: this.totalSize,
progress: this.progress,
uploadedSize: this.uploadedSize,
averageSpeed: this.averageSpeed,
timeRemaining: this.timeRemaining
});
}
pause() {
logger.info('** pause **');
this.isUploading = false;
const abortIndex = Object.keys(this.uploadTasks).map(v => v * 1);
abortIndex.forEach(index => {
this.chunksIndexNeedRead.push(index);
this.uploadTasks[index].abort();
});
this.uploadTasks = {};
}
resume() {
logger.info('** resume **');
this.isUploading = true;
this._upload();
}
cancel() {
logger.info('** cancel **');
this.pause();
this._reset();
}
_reset() {
this.chunksIndexNeedRead = Array.from(Array(this.totalChunks).keys());
this.chunksIndexNeedSend = Array.from(Array(this.totalChunks).keys());
this.chunksNeedSend = this.totalChunks;
this.sizeNeedSend = this.totalSize;
this.identifier = '';
this.chunksSend = 0;
this.chunksQueue = [];
this.uploadTasks = {};
this.pUploadList = [];
this.uploadedChunks = [];
this.isUploading = false;
this.isFail = false;
this.progress = 0;
this.uploadedSize = 0;
this.averageSpeed = 0;
this.timeRemaining = Number.POSITIVE_INFINITY;
this.dispatchProgress();
}
readFileChunk() {
const {
tempFilePath,
chunkSize,
maxLoadChunks,
chunksQueue,
chunksIndexNeedRead,
totalSize
} = this;
const chunks = Math.min(chunksIndexNeedRead.length, maxLoadChunks - chunksQueue.length);
// 异步读取
logger.debug(`readFileChunk chunks: ${chunks}, chunksIndexNeedRead`, this.chunksIndexNeedRead);
for (let i = 0; i < chunks; i++) {
const index = chunksIndexNeedRead.shift();
const position = index * chunkSize;
const length = Math.min(totalSize - position, chunkSize);
if (this.isFail) break
readFileAsync({
filePath: tempFilePath,
position,
length
}).then(res => {
const chunk = res.data;
this.chunksQueue.push({
chunk,
length,
index
});
this.uploadChunk();
return null
}).catch(e => {
this.handleFail({
errCode: 10001,
errMsg: e.errMsg
});
});
}
}
uploadChunk() { //暂停 / 继续 /取消
// 暂停中
if (!this.isUploading || this.isFail) return
// 没有更多数据了
if (!this.chunksQueue.length) return
// 达到最大并发度
if (Object.keys(this.uploadTasks).length === this.config.maxConcurrency) return
const {
chunk,
index,
length
} = this.chunksQueue.shift();
// 跳过已发送的分块
if (this.uploadedChunks.includes(index)) {
this.uploadChunk();
return
}
const {
uploadUrl,
query,
header
} = this.config;
const identifier = this.identifier;
const url = addParams(uploadUrl, {
...query,
identifier,
index,
chunkSize: length,
fileName: this.config.fileName,
totalChunks: this.totalChunks,
totalSize: this.totalSize
});
logger.debug(`uploadChunk index: ${index}, lenght ${length}`);
logger.time(`[Uploader] uploadChunk index-${index}`);
this._requestAsync({
url,
data: chunk,
header: {
...header,
'content-type': 'application/octet-stream'
},
method: 'POST',
}, (task) => {
this.uploadTasks[index] = task;
}).then((res) => {
if (res.data.result.status_info.status_code != 100) {
this.cancel();
return
}
this.chunksSend++;
delete this.uploadTasks[index];
this.updateUploadSize(length);
logger.debug(`uploadChunk success chunksSend: ${this.chunksSend}`);
logger.timeEnd(`[Uploader] uploadChunk index-${index}`);
// 尝试继续加载文件
this.readFileChunk();
// 尝试继续发送下一条
this.uploadChunk();
// 所有分片发送完毕
if (this.chunksSend === this.chunksNeedSend) {
this.emit('uploadDone');
}
return null
}).catch(res => {
if (res.errMsg.includes('request:fail abort')) {
logger.info(`chunk index-${index} will be aborted`);
} else {
this.handleFail({
errCode: 20002,
errMsg: res.errMsg
});
}
});
}
emit(event, data) {
this.emitter.emit(event, data);
}
on(event, listenr) {
this.emitter.on(event, listenr);
}
off(event, listenr) {
this.emitter.off(event, listenr);
}
generateIdentifier() {
let identifier = '';
const generator = this.config.generateIdentifier;
if (isFunction(generator)) {
identifier = generator();
} else {
const uuid = `${appId}-${Date.now()}-${Math.random()}`;
identifier = sparkMd5.hash(uuid);
}
return identifier
}
async computeMD5() {
const {
tempFilePath,
totalSize,
chunkSize
} = this;
// 文件比内存限制小时,保存分片
const isltMaxMemory = totalSize < this.config.maxMemory;
const sliceSize = isltMaxMemory ? chunkSize : 10 * MB;
const sliceNum = Math.ceil(totalSize / sliceSize);
const spark = new sparkMd5.ArrayBuffer();
for (let i = 0; i < sliceNum; i++) {
const position = i * sliceSize;
const length = Math.min(totalSize - position, sliceSize);
// eslint-disable-next-line no-await-in-loop
const [readFileErr, readFileResp] = await awaitWrap(readFileAsync({
filePath: tempFilePath,
position,
length
}));
if (readFileErr) {
spark.destroy();
throw (new Error(readFileErr.errMsg))
}
const chunk = readFileResp.data;
if (isltMaxMemory) {
this.chunksQueue.push({
chunk,
length,
index: i
});
}
spark.append(chunk);
}
this.chunksIndexNeedRead = [];
const identifier = spark.end();
spark.destroy();
return identifier
}
async verifyRequest() {
const {
verifyUrl,
fileName
} = this.config;
const verifyResp = await this._requestAsync({
url: verifyUrl,
data: {
fileName,
identifier: this.identifier
}
});
return verifyResp
}
async mergeRequest() {
const {
mergeUrl,
fileName
} = this.config;
const mergeResp = await this._requestAsync({
url: mergeUrl,
data: {
fileName,
identifier: this.identifier
}
});
return mergeResp
}
}
export default Uploader;
//# sourceMappingURL=uploader.js.map