vue-upload-component
Version:
Vue.js file upload component, Multi-file upload, Upload directory, Drag upload, Drag the directory, Upload multiple files at the same time, html4 (IE 9), `PUT` method, Customize the filter
1,876 lines (1,586 loc) • 60 kB
JavaScript
/*!
* Name: vue-upload-component
* Version: 2.8.22
* Author: LianYue
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.VueUploadComponent = factory());
}(this, (function () { 'use strict';
/**
* Creates a XHR request
*
* @param {Object} options
*/
var createRequest = function createRequest(options) {
var xhr = new XMLHttpRequest();
xhr.open(options.method || 'GET', options.url);
xhr.responseType = 'json';
if (options.headers) {
Object.keys(options.headers).forEach(function (key) {
xhr.setRequestHeader(key, options.headers[key]);
});
}
return xhr;
};
/**
* Sends a XHR request with certain body
*
* @param {XMLHttpRequest} xhr
* @param {Object} body
*/
var sendRequest = function sendRequest(xhr, body) {
return new Promise(function (resolve, reject) {
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
var response;
try {
response = JSON.parse(xhr.response);
} catch (err) {
response = xhr.response;
}
resolve(response);
} else {
reject(xhr.response);
}
};
xhr.onerror = function () {
return reject(xhr.response);
};
xhr.send(JSON.stringify(body));
});
};
/**
* Sends a XHR request with certain form data
*
* @param {XMLHttpRequest} xhr
* @param {Object} data
*/
var sendFormRequest = function sendFormRequest(xhr, data) {
var body = new FormData();
for (var name in data) {
body.append(name, data[name]);
}
return new Promise(function (resolve, reject) {
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
var response;
try {
response = JSON.parse(xhr.response);
} catch (err) {
response = xhr.response;
}
resolve(response);
} else {
reject(xhr.response);
}
};
xhr.onerror = function () {
return reject(xhr.response);
};
xhr.send(body);
});
};
/**
* Creates and sends XHR request
*
* @param {Object} options
*
* @returns Promise
*/
function request (options) {
var xhr = createRequest(options);
return sendRequest(xhr, options.body);
}
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var ChunkUploadHandler = function () {
/**
* Constructor
*
* @param {File} file
* @param {Object} options
*/
function ChunkUploadHandler(file, options) {
_classCallCheck(this, ChunkUploadHandler);
this.file = file;
this.options = options;
this.chunks = [];
this.sessionId = null;
this.chunkSize = null;
this.speedInterval = null;
}
/**
* Gets the max retries from options
*/
_createClass(ChunkUploadHandler, [{
key: 'createChunks',
/**
* Creates all the chunks in the initial state
*/
value: function createChunks() {
this.chunks = [];
var start = 0;
var end = this.chunkSize;
while (start < this.fileSize) {
this.chunks.push({
blob: this.file.file.slice(start, end),
startOffset: start,
active: false,
retries: this.maxRetries
});
start = end;
end = start + this.chunkSize;
}
}
/**
* Updates the progress of the file with the handler's progress
*/
}, {
key: 'updateFileProgress',
value: function updateFileProgress() {
this.file.progress = this.progress;
}
/**
* Paues the upload process
* - Stops all active requests
* - Sets the file not active
*/
}, {
key: 'pause',
value: function pause() {
this.file.active = false;
this.stopChunks();
}
/**
* Stops all the current chunks
*/
}, {
key: 'stopChunks',
value: function stopChunks() {
this.chunksUploading.forEach(function (chunk) {
chunk.xhr.abort();
chunk.active = false;
});
this.stopSpeedCalc();
}
/**
* Resumes the file upload
* - Sets the file active
* - Starts the following chunks
*/
}, {
key: 'resume',
value: function resume() {
this.file.active = true;
this.startChunking();
}
/**
* Starts the file upload
*
* @returns Promise
* - resolve The file was uploaded
* - reject The file upload failed
*/
}, {
key: 'upload',
value: function upload() {
var _this = this;
this.promise = new Promise(function (resolve, reject) {
_this.resolve = resolve;
_this.reject = reject;
});
this.start();
return this.promise;
}
/**
* Start phase
* Sends a request to the backend to initialise the chunks
*/
}, {
key: 'start',
value: function start() {
var _this2 = this;
request({
method: 'POST',
headers: Object.assign({}, this.headers, {
'Content-Type': 'application/json'
}),
url: this.action,
body: Object.assign(this.startBody, {
phase: 'start',
mime_type: this.fileType,
size: this.fileSize,
name: this.fileName
})
}).then(function (res) {
if (res.status !== 'success') {
_this2.file.response = res;
return _this2.reject('server');
}
_this2.sessionId = res.data.session_id;
_this2.chunkSize = res.data.end_offset;
_this2.createChunks();
_this2.startChunking();
}).catch(function (res) {
_this2.file.response = res;
_this2.reject('server');
});
}
/**
* Starts to upload chunks
*/
}, {
key: 'startChunking',
value: function startChunking() {
for (var i = 0; i < this.maxActiveChunks; i++) {
this.uploadNextChunk();
}
this.startSpeedCalc();
}
/**
* Uploads the next chunk
* - Won't do anything if the process is paused
* - Will start finish phase if there are no more chunks to upload
*/
}, {
key: 'uploadNextChunk',
value: function uploadNextChunk() {
if (this.file.active) {
if (this.hasChunksToUpload) {
return this.uploadChunk(this.chunksToUpload[0]);
}
if (this.chunksUploading.length === 0) {
return this.finish();
}
}
}
/**
* Uploads a chunk
* - Sends the chunk to the backend
* - Sets the chunk as uploaded if everything went well
* - Decreases the number of retries if anything went wrong
* - Fails if there are no more retries
*
* @param {Object} chunk
*/
}, {
key: 'uploadChunk',
value: function uploadChunk(chunk) {
var _this3 = this;
chunk.progress = 0;
chunk.active = true;
this.updateFileProgress();
chunk.xhr = createRequest({
method: 'POST',
headers: this.headers,
url: this.action
});
chunk.xhr.upload.addEventListener('progress', function (evt) {
if (evt.lengthComputable) {
chunk.progress = Math.round(evt.loaded / evt.total * 100);
}
}, false);
sendFormRequest(chunk.xhr, Object.assign(this.uploadBody, {
phase: 'upload',
session_id: this.sessionId,
start_offset: chunk.startOffset,
chunk: chunk.blob
})).then(function (res) {
chunk.active = false;
if (res.status === 'success') {
chunk.uploaded = true;
} else {
if (chunk.retries-- <= 0) {
_this3.stopChunks();
return _this3.reject('upload');
}
}
_this3.uploadNextChunk();
}).catch(function () {
chunk.active = false;
if (chunk.retries-- <= 0) {
_this3.stopChunks();
return _this3.reject('upload');
}
_this3.uploadNextChunk();
});
}
/**
* Finish phase
* Sends a request to the backend to finish the process
*/
}, {
key: 'finish',
value: function finish() {
var _this4 = this;
this.updateFileProgress();
this.stopSpeedCalc();
request({
method: 'POST',
headers: Object.assign({}, this.headers, {
'Content-Type': 'application/json'
}),
url: this.action,
body: Object.assign(this.finishBody, {
phase: 'finish',
session_id: this.sessionId
})
}).then(function (res) {
_this4.file.response = res;
if (res.status !== 'success') {
return _this4.reject('server');
}
_this4.resolve(res);
}).catch(function (res) {
_this4.file.response = res;
_this4.reject('server');
});
}
/**
* Sets an interval to calculate and
* set upload speed every 3 seconds
*/
}, {
key: 'startSpeedCalc',
value: function startSpeedCalc() {
var _this5 = this;
this.file.speed = 0;
var lastUploadedBytes = 0;
if (!this.speedInterval) {
this.speedInterval = window.setInterval(function () {
var uploadedBytes = _this5.progress / 100 * _this5.fileSize;
_this5.file.speed = uploadedBytes - lastUploadedBytes;
lastUploadedBytes = uploadedBytes;
}, 1000);
}
}
/**
* Removes the upload speed interval
*/
}, {
key: 'stopSpeedCalc',
value: function stopSpeedCalc() {
this.speedInterval && window.clearInterval(this.speedInterval);
this.speedInterval = null;
this.file.speed = 0;
}
}, {
key: 'maxRetries',
get: function get() {
return parseInt(this.options.maxRetries, 10);
}
/**
* Gets the max number of active chunks being uploaded at once from options
*/
}, {
key: 'maxActiveChunks',
get: function get() {
return parseInt(this.options.maxActive, 10);
}
/**
* Gets the file type
*/
}, {
key: 'fileType',
get: function get() {
return this.file.type;
}
/**
* Gets the file size
*/
}, {
key: 'fileSize',
get: function get() {
return this.file.size;
}
/**
* Gets the file name
*/
}, {
key: 'fileName',
get: function get() {
return this.file.name;
}
/**
* Gets action (url) to upload the file
*/
}, {
key: 'action',
get: function get() {
return this.options.action || null;
}
/**
* Gets the body to be merged when sending the request in start phase
*/
}, {
key: 'startBody',
get: function get() {
return this.options.startBody || {};
}
/**
* Gets the body to be merged when sending the request in upload phase
*/
}, {
key: 'uploadBody',
get: function get() {
return this.options.uploadBody || {};
}
/**
* Gets the body to be merged when sending the request in finish phase
*/
}, {
key: 'finishBody',
get: function get() {
return this.options.finishBody || {};
}
/**
* Gets the headers of the requests from options
*/
}, {
key: 'headers',
get: function get() {
return this.options.headers || {};
}
/**
* Whether it's ready to upload files or not
*/
}, {
key: 'readyToUpload',
get: function get() {
return !!this.chunks;
}
/**
* Gets the progress of the chunk upload
* - Gets all the completed chunks
* - Gets the progress of all the chunks that are being uploaded
*/
}, {
key: 'progress',
get: function get() {
var _this6 = this;
var completedProgress = this.chunksUploaded.length / this.chunks.length * 100;
var uploadingProgress = this.chunksUploading.reduce(function (progress, chunk) {
return progress + (chunk.progress | 0) / _this6.chunks.length;
}, 0);
return Math.min(completedProgress + uploadingProgress, 100);
}
/**
* Gets all the chunks that are pending to be uploaded
*/
}, {
key: 'chunksToUpload',
get: function get() {
return this.chunks.filter(function (chunk) {
return !chunk.active && !chunk.uploaded;
});
}
/**
* Whether there are chunks to upload or not
*/
}, {
key: 'hasChunksToUpload',
get: function get() {
return this.chunksToUpload.length > 0;
}
/**
* Gets all the chunks that are uploading
*/
}, {
key: 'chunksUploading',
get: function get() {
return this.chunks.filter(function (chunk) {
return !!chunk.xhr && !!chunk.active;
});
}
/**
* Gets all the chunks that have finished uploading
*/
}, {
key: 'chunksUploaded',
get: function get() {
return this.chunks.filter(function (chunk) {
return !!chunk.uploaded;
});
}
}]);
return ChunkUploadHandler;
}();
//
//
//
//
//
//
//
//
//
//
//
//
//
//
var script = {
methods: {
change: function change(e) {
this.$parent.addInputFile(e.target);
if (e.target.files) {
e.target.value = '';
if (e.target.files.length && !/safari/i.test(navigator.userAgent)) {
e.target.type = '';
e.target.type = 'file';
}
} else {
// ie9 fix #219
this.$destroy();
// eslint-disable-next-line
new this.constructor({
parent: this.$parent,
el: this.$el
});
}
}
}
};
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
/* server only */
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
if (typeof shadowMode !== 'boolean') {
createInjectorSSR = createInjector;
createInjector = shadowMode;
shadowMode = false;
} // Vue.extend constructor export interop.
var options = typeof script === 'function' ? script.options : script; // render functions
if (template && template.render) {
options.render = template.render;
options.staticRenderFns = template.staticRenderFns;
options._compiled = true; // functional template
if (isFunctionalTemplate) {
options.functional = true;
}
} // scopedId
if (scopeId) {
options._scopeId = scopeId;
}
var hook;
if (moduleIdentifier) {
// server build
hook = function hook(context) {
// 2.3 injection
context = context || // cached call
this.$vnode && this.$vnode.ssrContext || // stateful
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__;
} // inject component styles
if (style) {
style.call(this, createInjectorSSR(context));
} // register component module identifier for async chunk inference
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier);
}
}; // used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook;
} else if (style) {
hook = shadowMode ? function () {
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot));
} : function (context) {
style.call(this, createInjector(context));
};
}
if (hook) {
if (options.functional) {
// register for functional component in vue file
var originalRender = options.render;
options.render = function renderWithStyleInjection(h, context) {
hook.call(context);
return originalRender(h, context);
};
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate;
options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
}
}
return script;
}
var normalizeComponent_1 = normalizeComponent;
/* script */
var __vue_script__ = script;
/* template */
var __vue_render__ = function __vue_render__() {
var _vm = this;var _h = _vm.$createElement;var _c = _vm._self._c || _h;return _c('input', { attrs: { "type": "file", "name": _vm.$parent.name, "id": _vm.$parent.inputId || _vm.$parent.name, "accept": _vm.$parent.accept, "capture": _vm.$parent.capture, "disabled": _vm.$parent.disabled, "webkitdirectory": _vm.$parent.directory && _vm.$parent.features.directory ? true : undefined, "directory": _vm.$parent.directory && _vm.$parent.features.directory ? true : undefined, "multiple": _vm.$parent.multiple && _vm.$parent.features.html5 }, on: { "change": _vm.change } });
};
var __vue_staticRenderFns__ = [];
/* style */
var __vue_inject_styles__ = undefined;
/* scoped */
var __vue_scope_id__ = undefined;
/* module identifier */
var __vue_module_identifier__ = undefined;
/* functional template */
var __vue_is_functional_template__ = false;
/* style inject */
/* style inject SSR */
var InputFile = normalizeComponent_1({ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, undefined, undefined);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var CHUNK_DEFAULT_OPTIONS = {
headers: {},
action: '',
minSize: 1048576,
maxActive: 3,
maxRetries: 5,
handler: ChunkUploadHandler
};
var script$1 = {
components: {
InputFile: InputFile
},
props: {
inputId: {
type: String
},
name: {
type: String,
default: 'file'
},
accept: {
type: String
},
capture: {},
disabled: {},
multiple: {
type: Boolean
},
maximum: {
type: Number,
default: function _default() {
return this.multiple ? 0 : 1;
}
},
addIndex: {
type: [Boolean, Number]
},
directory: {
type: Boolean
},
postAction: {
type: String
},
putAction: {
type: String
},
customAction: {
type: Function
},
headers: {
type: Object,
default: Object
},
data: {
type: Object,
default: Object
},
timeout: {
type: Number,
default: 0
},
drop: {
default: false
},
dropDirectory: {
type: Boolean,
default: true
},
size: {
type: Number,
default: 0
},
extensions: {
default: Array
},
value: {
type: Array,
default: Array
},
thread: {
type: Number,
default: 1
},
// Chunk upload enabled
chunkEnabled: {
type: Boolean,
default: false
},
// Chunk upload properties
chunk: {
type: Object,
default: function _default() {
return CHUNK_DEFAULT_OPTIONS;
}
}
},
data: function data() {
return {
files: this.value,
features: {
html5: true,
directory: false,
drop: false
},
active: false,
dropActive: false,
uploading: 0,
destroy: false
};
},
/**
* mounted
* @return {[type]} [description]
*/
mounted: function mounted() {
var input = document.createElement('input');
input.type = 'file';
input.multiple = true;
// html5 特征
if (window.FormData && input.files) {
// 上传目录特征
if (typeof input.webkitdirectory === 'boolean' || typeof input.directory === 'boolean') {
this.features.directory = true;
}
// 拖拽特征
if (this.features.html5 && typeof input.ondrop !== 'undefined') {
this.features.drop = true;
}
} else {
this.features.html5 = false;
}
// files 定位缓存
this.maps = {};
if (this.files) {
for (var i = 0; i < this.files.length; i++) {
var file = this.files[i];
this.maps[file.id] = file;
}
}
this.$nextTick(function () {
var _this = this;
// 更新下父级
if (this.$parent) {
this.$parent.$forceUpdate();
// 拖拽渲染
this.$parent.$nextTick(function () {
_this.watchDrop(_this.drop);
});
} else {
// 拖拽渲染
this.watchDrop(this.drop);
}
});
},
/**
* beforeDestroy
* @return {[type]} [description]
*/
beforeDestroy: function beforeDestroy() {
// 已销毁
this.destroy = true;
// 设置成不激活
this.active = false;
// 销毁拖拽事件
this.watchDrop(false);
},
computed: {
/**
* uploading 正在上传的线程
* @return {[type]} [description]
*/
/**
* uploaded 文件列表是否全部已上传
* @return {[type]} [description]
*/
uploaded: function uploaded() {
var file = void 0;
for (var i = 0; i < this.files.length; i++) {
file = this.files[i];
if (file.fileObject && !file.error && !file.success) {
return false;
}
}
return true;
},
chunkOptions: function chunkOptions() {
return Object.assign(CHUNK_DEFAULT_OPTIONS, this.chunk);
},
className: function className() {
return ['file-uploads', this.features.html5 ? 'file-uploads-html5' : 'file-uploads-html4', this.features.directory && this.directory ? 'file-uploads-directory' : undefined, this.features.drop && this.drop ? 'file-uploads-drop' : undefined, this.disabled ? 'file-uploads-disabled' : undefined];
}
},
watch: {
active: function active(_active) {
this.watchActive(_active);
},
dropActive: function dropActive(value) {
this.watchDropActive(value);
if (this.$parent) {
this.$parent.$forceUpdate();
}
},
drop: function drop(value) {
this.watchDrop(value);
},
value: function value(files) {
if (this.files === files) {
return;
}
this.files = files;
var oldMaps = this.maps;
// 重写 maps 缓存
this.maps = {};
for (var i = 0; i < this.files.length; i++) {
var file = this.files[i];
this.maps[file.id] = file;
}
// add, update
for (var key in this.maps) {
var newFile = this.maps[key];
var oldFile = oldMaps[key];
if (newFile !== oldFile) {
this.emitFile(newFile, oldFile);
}
}
// delete
for (var _key in oldMaps) {
if (!this.maps[_key]) {
this.emitFile(undefined, oldMaps[_key]);
}
}
}
},
methods: {
// 清空
clear: function clear() {
if (this.files.length) {
var files = this.files;
this.files = [];
// 定位
this.maps = {};
// 事件
this.emitInput();
for (var i = 0; i < files.length; i++) {
this.emitFile(undefined, files[i]);
}
}
return true;
},
// 选择
get: function get(id) {
if (!id) {
return false;
}
if ((typeof id === 'undefined' ? 'undefined' : _typeof(id)) === 'object') {
return this.maps[id.id] || false;
}
return this.maps[id] || false;
},
// 添加
add: function add(_files) {
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.addIndex;
var files = _files;
var isArray = files instanceof Array;
// 不是数组整理成数组
if (!isArray) {
files = [files];
}
// 遍历规范对象
var addFiles = [];
for (var i = 0; i < files.length; i++) {
var file = files[i];
if (this.features.html5 && file instanceof Blob) {
file = {
file: file,
size: file.size,
name: file.webkitRelativePath || file.relativePath || file.name || 'unknown',
type: file.type
};
}
var fileObject = false;
if (file.fileObject === false) ; else if (file.fileObject) {
fileObject = true;
} else if (typeof Element !== 'undefined' && file.el instanceof Element) {
fileObject = true;
} else if (typeof Blob !== 'undefined' && file.file instanceof Blob) {
fileObject = true;
}
if (fileObject) {
file = _extends({
fileObject: true,
size: -1,
name: 'Filename',
type: '',
active: false,
error: '',
success: false,
putAction: this.putAction,
postAction: this.postAction,
timeout: this.timeout
}, file, {
response: {},
progress: '0.00', // 只读
speed: 0 // 只读
// xhr: false, // 只读
// iframe: false, // 只读
});
file.data = _extends({}, this.data, file.data ? file.data : {});
file.headers = _extends({}, this.headers, file.headers ? file.headers : {});
}
// 必须包含 id
if (!file.id) {
file.id = Math.random().toString(36).substr(2);
}
if (this.emitFilter(file, undefined)) {
continue;
}
// 最大数量限制
if (this.maximum > 1 && addFiles.length + this.files.length >= this.maximum) {
break;
}
addFiles.push(file);
// 最大数量限制
if (this.maximum === 1) {
break;
}
}
// 没有文件
if (!addFiles.length) {
return false;
}
// 如果是 1 清空
if (this.maximum === 1) {
this.clear();
}
// 添加进去 files
var newFiles = void 0;
if (index === true || index === 0) {
newFiles = addFiles.concat(this.files);
} else if (index) {
var _newFiles;
newFiles = this.files.concat([]);
(_newFiles = newFiles).splice.apply(_newFiles, [index, 0].concat(addFiles));
} else {
newFiles = this.files.concat(addFiles);
}
this.files = newFiles;
// 定位
for (var _i = 0; _i < addFiles.length; _i++) {
var _file2 = addFiles[_i];
this.maps[_file2.id] = _file2;
}
// 事件
this.emitInput();
for (var _i2 = 0; _i2 < addFiles.length; _i2++) {
this.emitFile(addFiles[_i2], undefined);
}
return isArray ? addFiles : addFiles[0];
},
// 添加表单文件
addInputFile: function addInputFile(el) {
var files = [];
if (el.files) {
for (var i = 0; i < el.files.length; i++) {
var file = el.files[i];
files.push({
size: file.size,
name: file.webkitRelativePath || file.relativePath || file.name,
type: file.type,
file: file
});
}
} else {
var names = el.value.replace(/\\/g, '/').split('/');
delete el.__vuex__;
files.push({
name: names[names.length - 1],
el: el
});
}
return this.add(files);
},
// 添加 DataTransfer
addDataTransfer: function addDataTransfer(dataTransfer) {
var _this2 = this;
var files = [];
if (dataTransfer.items && dataTransfer.items.length) {
var items = [];
for (var i = 0; i < dataTransfer.items.length; i++) {
var item = dataTransfer.items[i];
if (item.getAsEntry) {
item = item.getAsEntry() || item.getAsFile();
} else if (item.webkitGetAsEntry) {
item = item.webkitGetAsEntry() || item.getAsFile();
} else {
item = item.getAsFile();
}
if (item) {
items.push(item);
}
}
return new Promise(function (resolve, reject) {
var forEach = function forEach(i) {
var item = items[i];
// 结束 文件数量大于 最大数量
if (!item || _this2.maximum > 0 && files.length >= _this2.maximum) {
return resolve(_this2.add(files));
}
_this2.getEntry(item).then(function (results) {
files.push.apply(files, _toConsumableArray(results));
forEach(i + 1);
});
};
forEach(0);
});
}
if (dataTransfer.files.length) {
for (var _i3 = 0; _i3 < dataTransfer.files.length; _i3++) {
files.push(dataTransfer.files[_i3]);
if (this.maximum > 0 && files.length >= this.maximum) {
break;
}
}
return Promise.resolve(this.add(files));
}
return Promise.resolve([]);
},
// 获得 entry
getEntry: function getEntry(entry) {
var _this3 = this;
var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
return new Promise(function (resolve, reject) {
if (entry.isFile) {
entry.file(function (file) {
resolve([{
size: file.size,
name: path + file.name,
type: file.type,
file: file
}]);
});
} else if (entry.isDirectory && _this3.dropDirectory) {
var files = [];
var dirReader = entry.createReader();
var readEntries = function readEntries() {
dirReader.readEntries(function (entries) {
var forEach = function forEach(i) {
if (!entries[i] && i === 0 || _this3.maximum > 0 && files.length >= _this3.maximum) {
return resolve(files);
}
if (!entries[i]) {
return readEntries();
}
_this3.getEntry(entries[i], path + entry.name + '/').then(function (results) {
files.push.apply(files, _toConsumableArray(results));
forEach(i + 1);
});
};
forEach(0);
});
};
readEntries();
} else {
resolve([]);
}
});
},
replace: function replace(id1, id2) {
var file1 = this.get(id1);
var file2 = this.get(id2);
if (!file1 || !file2 || file1 === file2) {
return false;
}
var files = this.files.concat([]);
var index1 = files.indexOf(file1);
var index2 = files.indexOf(file2);
if (index1 === -1 || index2 === -1) {
return false;
}
files[index1] = file2;
files[index2] = file1;
this.files = files;
this.emitInput();
return true;
},
// 移除
remove: function remove(id) {
var file = this.get(id);
if (file) {
if (this.emitFilter(undefined, file)) {
return false;
}
var files = this.files.concat([]);
var index = files.indexOf(file);
if (index === -1) {
console.error('remove', file);
return false;
}
files.splice(index, 1);
this.files = files;
// 定位
delete this.maps[file.id];
// 事件
this.emitInput();
this.emitFile(undefined, file);
}
return file;
},
// 更新
update: function update(id, data) {
var file = this.get(id);
if (file) {
var newFile = _extends({}, file, data);
// 停用必须加上错误
if (file.fileObject && file.active && !newFile.active && !newFile.error && !newFile.success) {
newFile.error = 'abort';
}
if (this.emitFilter(newFile, file)) {
return false;
}
var files = this.files.concat([]);
var index = files.indexOf(file);
if (index === -1) {
console.error('update', file);
return false;
}
files.splice(index, 1, newFile);
this.files = files;
// 删除 旧定位 写入 新定位 (已便支持修改id)
delete this.maps[file.id];
this.maps[newFile.id] = newFile;
// 事件
this.emitInput();
this.emitFile(newFile, file);
return newFile;
}
return false;
},
// 预处理 事件 过滤器
emitFilter: function emitFilter(newFile, oldFile) {
var isPrevent = false;
this.$emit('input-filter', newFile, oldFile, function () {
isPrevent = true;
return isPrevent;
});
return isPrevent;
},
// 处理后 事件 分发
emitFile: function emitFile(newFile, oldFile) {
this.$emit('input-file', newFile, oldFile);
if (newFile && newFile.fileObject && newFile.active && (!oldFile || !oldFile.active)) {
this.uploading++;
// 激活
this.$nextTick(function () {
var _this4 = this;
setTimeout(function () {
_this4.upload(newFile).then(function () {
// eslint-disable-next-line
newFile = _this4.get(newFile);
if (newFile && newFile.fileObject) {
_this4.update(newFile, {
active: false,
success: !newFile.error
});
}
}).catch(function (e) {
_this4.update(newFile, {
active: false,
success: false,
error: e.code || e.error || e.message || e
});
});
}, parseInt(Math.random() * 50 + 50, 10));
});
} else if ((!newFile || !newFile.fileObject || !newFile.active) && oldFile && oldFile.fileObject && oldFile.active) {
// 停止
this.uploading--;
}
// 自动延续激活
if (this.active && (Boolean(newFile) !== Boolean(oldFile) || newFile.active !== oldFile.active)) {
this.watchActive(true);
}
},
emitInput: function emitInput() {
this.$emit('input', this.files);
},
// 上传
upload: function upload(id) {
var file = this.get(id);
// 被删除
if (!file) {
return Promise.reject('not_exists');
}
// 不是文件对象
if (!file.fileObject) {
return Promise.reject('file_object');
}
// 有错误直接响应
if (file.error) {
return Promise.reject(file.error);
}
// 已完成直接响应
if (file.success) {
return Promise.resolve(file);
}
// 后缀
var extensions = this.extensions;
if (extensions && (extensions.length || typeof extensions.length === 'undefined')) {
if ((typeof extensions === 'undefined' ? 'undefined' : _typeof(extensions)) !== 'object' || !(extensions instanceof RegExp)) {
if (typeof extensions === 'string') {
extensions = extensions.split(',').map(function (value) {
return value.trim();
}).filter(function (value) {
return value;
});
}
extensions = new RegExp('\\.(' + extensions.join('|').replace(/\./g, '\\.') + ')$', 'i');
}
if (file.name.search(extensions) === -1) {
return Promise.reject('extension');
}
}
// 大小
if (this.size > 0 && file.size >= 0 && file.size > this.size) {
return Promise.reject('size');
}
if (this.customAction) {
return this.customAction(file, this);
}
if (this.features.html5) {
if (this.shouldUseChunkUpload(file)) {
return this.uploadChunk(file);
}
if (file.putAction) {
return this.uploadPut(file);
}
if (file.postAction) {
return this.uploadHtml5(file);
}
}
if (file.postAction) {
return this.uploadHtml4(file);
}
return Promise.reject('No action configured');
},
/**
* Whether this file should be uploaded using chunk upload or not
*
* @param Object file
*/
shouldUseChunkUpload: function shouldUseChunkUpload(file) {
return this.chunkEnabled && !!this.chunkOptions.handler && file.size > this.chunkOptions.minSize;
},
/**
* Upload a file using Chunk method
*
* @param File file
*/
uploadChunk: function uploadChunk(file) {
var HandlerClass = this.chunkOptions.handler;
file.chunk = new HandlerClass(file, this.chunkOptions);
return file.chunk.upload();
},
uploadPut: function uploadPut(file) {
var querys = [];
var value = void 0;
for (var key in file.data) {
value = file.data[key];
if (value !== null && value !== undefined) {
querys.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
}
}
var queryString = querys.length ? (file.putAction.indexOf('?') === -1 ? '?' : '&') + querys.join('&') : '';
var xhr = new XMLHttpRequest();
xhr.open('PUT', file.putAction + queryString);
return this.uploadXhr(xhr, file, file.file);
},
uploadHtml5: function uploadHtml5(file) {
var form = new window.FormData();
var value = void 0;
for (var key in file.data) {
value = file.data[key];
if (value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && typeof value.toString !== 'function') {
if (value instanceof File) {
form.append(key, value, value.name);
} else {
form.append(key, JSON.stringify(value));
}
} else if (value !== null && value !== undefined) {
form.append(key, value);
}
}
form.append(this.name, file.file, file.file.filename || file.name);
var xhr = new XMLHttpRequest();
xhr.open('POST', file.postAction);
return this.uploadXhr(xhr, file, form);
},
uploadXhr: function uploadXhr(xhr, _file, body) {
var _this5 = this;
var file = _file;
var speedTime = 0;
var speedLoaded = 0;
// 进度条
xhr.upload.onprogress = function (e) {
// 还未开始上传 已删除 未激活
file = _this5.get(file);
if (!e.lengthComputable || !file || !file.fileObject || !file.active) {
return;
}
// 进度 速度 每秒更新一次
var speedTime2 = Math.round(Date.now() / 1000);
if (speedTime2 === speedTime) {
return;
}
speedTime = speedTime2;
file = _this5.update(file, {
progress: (e.loaded / e.total * 100).toFixed(2),
speed: e.loaded - speedLoaded
});
speedLoaded = e.loaded;
};
// 检查激活状态
var interval = setInterval(function () {
file = _this5.get(file);
if (file && file.fileObject && !file.success && !file.error && file.active) {
return;
}
if (interval) {
clearInterval(interval);
interval = false;
}
try {
xhr.abort();
xhr.timeout = 1;
} catch (e) {}
}, 100);
return new Promise(function (resolve, reject) {
var complete = void 0;
var fn = function fn(e) {
// 已经处理过了
if (complete) {
return;
}
complete = true;
if (interval) {
clearInterval(interval);
interval = false;
}
file = _this5.get(file);
// 不存在直接响应
if (!file) {
return reject('not_exists');
}
// 不是文件对象
if (!file.fileObject) {
return reject('file_object');
}
// 有错误自动响应
if (file.error) {
return reject(file.error);
}
// 未激活
if (!file.active) {
return reject('abort');
}
// 已完成 直接相应
if (file.success) {
return resolve(file);
}
var data = {};
switch (e.type) {
case 'timeout':
case 'abort':
data.error = e.type;
break;
case 'error':
if (!xhr.status) {
data.error = 'network';
} else if (xhr.status >= 500) {
data.error = 'server';
} else if (xhr.status >= 400) {
data.error = 'denied';
}
break;
default:
if (xhr.status >= 500) {
data.error = 'server';
} else if (xhr.status >= 400) {
data.error = 'denied';
} else {
data.progress = '100.00';
}
}
if (xhr.responseText) {
var contentType = xhr.getResponseHeader('Content-Type');
if (contentType && contentType.indexOf('/json') !== -1) {
data.response = JSON.parse(xhr.responseText);
} else {
data.response = xhr.responseText;
}
}
// 更新
file = _this5.update(file, data);
// 相应错误
if (file.error) {
return reject(file.error);
}
// 响应
return resolve(file);
};
// 事件
xhr.onload = fn;
xhr.onerror = fn;
xhr.onabort = fn;
xhr.ontimeout = fn;
// 超时
if (file.timeout) {
xhr.timeout = file.timeout;
}
// headers
for (var key in file.headers) {
xhr.setRequestHeader(key, file.headers[key]);
}
// 更新 xhr
file = _this5.update(file, { xhr: xhr });
// 开始上传
xhr.send(body);
});
},
uploadHtml4: function uploadHtml4(_file) {
var _this6 = this;
var file = _file;
var onKeydown = function onKeydown(e) {
if (e.keyCode === 27) {
e.preventDefault();
}
};
var iframe = document.createElement('iframe');
iframe.id = 'upload-iframe-' + file.id;
iframe.name = 'upload-iframe-' + file.id;
iframe.src = 'about:blank';
iframe.setAttribute('style', 'width:1px;height:1px;top:-999em;position:absolute; margin-top:-999em;');
var form = document.createElement('form');
form.action = file.postAction;
form.name = 'upload-form-' + file.id;
form.setAttribute('method', 'POST');
form.setAttribute('target', 'upload-iframe-' + file.id);
form.setAttribute('enctype', 'multipart/form-data');
var value = void 0;
var input = void 0;
for (var key in file.data) {
value = file.data[key];
if (value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && typeof value.toString !== 'function') {
value = JSON.stringify(value);
}
if (value !== null && value !== undefined) {
input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = value;
form.appendChild(input);
}
}
form.appendChild(file.el);
document.body.appendChild(iframe).appendChild(form);
var getResponseData = function getResponseData() {
var doc = void 0;
try {
if (iframe.contentWindow) {
doc = iframe.contentWindow.document;
}
} catch (err) {}
if (!doc) {
try {
doc = iframe.contentDocument ? iframe.contentDocument : iframe.document;
} catch (err) {
doc = iframe.document;
}
}
if (doc && doc.body) {
return doc.body.innerHTML;
}
return null;
};
return new Promise(function (resolve, reject) {
setTimeout(function () {
file = _this6.update(file, { iframe: iframe });
// 不存在
if (!file) {
return reject('not_exists');
}
// 定时检查
var interval = setInterval(function () {
file = _this6.get(file);
if (file && file.fileObject && !file.success && !file.error && file.active) {
return;
}
if (interval) {
clearInterval(interval);
interval = false;
}
iframe.onabort({ type: file ? 'abort' : 'not_exists' });
}, 100);
var complete = void 0;
var fn = function fn(e) {
// 已经处理过了
if (complete) {
return;
}
complete = true;
if (interval) {
clearInterval(interval);
interval = false;
}
// 关闭 esc 事件
document.body.removeEventListener('keydown', onKeydown);
file = _this6.get(file);
// 不存在直接响应
if (!file) {
return reject('not_exists');
}
// 不是文件对象
if (!file.fileObject) {
return reject('file_object');
}
// 有错误自动响应
if (file.error) {
return reject(file.error);
}
// 未激活
if (!file.active) {
return reject('abort');
}
// 已完成 直接相应
if (file.success) {
return resolve(file);
}
var response = getResponseData();
var data = {};
switch (e.type) {
case 'abort':
data.error = 'abort';
break;
case 'error':
if (file.error) {
data.error = file.error;
} else if (response === null) {
data.error = 'network';
} else {
data.error = 'denied';
}
break;
default:
if (file.error) {
data.erro