You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
674 lines
22 KiB
674 lines
22 KiB
<template>
|
|
<div>
|
|
<label :for="id||name" :class="{'file-uploads-label': true, 'file-uploads-html5': $mode == 'html5', 'file-uploads-html4': $mode == 'html4'}">
|
|
<span>{{title}}</span>
|
|
<input-file></input-file>
|
|
</label>
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
.file-uploads-label {
|
|
overflow: hidden;
|
|
position: relative;
|
|
text-align: center;
|
|
}
|
|
.file-uploads-label span{
|
|
-webkit-user-select: none;
|
|
-moz-user-select: none;
|
|
-ms-user-select: none;
|
|
-o-user-select: none;
|
|
user-select: none;
|
|
}
|
|
.file-uploads-label input{
|
|
z-index: 1;
|
|
opacity: 0;
|
|
font-size: 20em;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.file-uploads-label.file-uploads-html5 input{
|
|
width: 1px !important;
|
|
height: 1px !important;
|
|
top:-1px !important;
|
|
left:-1px !important;
|
|
right:auto !important;
|
|
bottom:auto !important;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
|
|
const createId = function() {
|
|
return Math.random().toString(36).substr(2);
|
|
}
|
|
|
|
export default {
|
|
props: {
|
|
title: {
|
|
type: String,
|
|
default: 'Upload file',
|
|
},
|
|
name: {
|
|
type: String,
|
|
default: 'file',
|
|
},
|
|
drop: {
|
|
default: false,
|
|
},
|
|
extensions: {
|
|
},
|
|
postAction: {
|
|
type: String,
|
|
},
|
|
putAction: {
|
|
type: String,
|
|
},
|
|
accept: {
|
|
type:String,
|
|
},
|
|
multiple: {
|
|
type: Boolean,
|
|
},
|
|
timeout: {
|
|
type: Number,
|
|
},
|
|
size: {
|
|
type: Number,
|
|
},
|
|
},
|
|
|
|
components: {
|
|
inputFile : {
|
|
template: '<input type="file" :name="$parent.name" :id="$parent.id||$parent.name" :accept="$parent.accept" @change="change" :multiple="$parent.multiple && $parent.$mode == \'html5\'">',
|
|
methods: {
|
|
change(e) {
|
|
this.$parent._addFileUploads(e.target);
|
|
this.$destroy();
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
files: [],
|
|
active: false,
|
|
uploaded: true,
|
|
dropActive: false,
|
|
dropElement: false,
|
|
request: {
|
|
data: {},
|
|
headers: {},
|
|
},
|
|
}
|
|
},
|
|
|
|
ready() {
|
|
this._drop(this.drop);
|
|
},
|
|
|
|
|
|
init() {
|
|
var input = document.createElement('input');
|
|
input.type = 'file';
|
|
if (window.FormData && input.files) {
|
|
this.$mode = 'html5';
|
|
} else {
|
|
this.$mode = 'html4';
|
|
}
|
|
this._index = 0;
|
|
this._dropActive = 0;
|
|
this._files = {};
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
|
this.active = false;
|
|
this.files = [];
|
|
},
|
|
|
|
watch: {
|
|
drop(value) {
|
|
this._drop(value);
|
|
},
|
|
files(files) {
|
|
var ids = [];
|
|
for (var i = 0; i < files.length; i++) {
|
|
var file = files[i];
|
|
if (!file.errno && !file.success) {
|
|
this.uploaded = false;
|
|
}
|
|
ids.push(file.id);
|
|
}
|
|
for (var id in this._files) {
|
|
if (ids.indexOf(id) != -1) {
|
|
continue;
|
|
}
|
|
|
|
var file = this._files[id]._file;
|
|
|
|
file.removed = true;
|
|
var xhr = this._files[id].xhr;
|
|
if (xhr) {
|
|
xhr.abort();
|
|
xhr.timeout = 1;
|
|
}
|
|
var iframe = this._files[id].iframe;
|
|
if (iframe) {
|
|
iframe.onabort({type:'abort'});
|
|
}
|
|
delete this._files[id];
|
|
this.$dispatch('removeFileUpload', file, this);
|
|
}
|
|
this._index = 0;
|
|
},
|
|
|
|
active(newValue, oldValue) {
|
|
if (newValue && !oldValue) {
|
|
this._fileUploads();
|
|
}
|
|
},
|
|
|
|
uploaded(uploaded) {
|
|
if (uploaded) {
|
|
this.active = false;
|
|
}
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
_drop(value) {
|
|
if (this.dropElement && this.$mode === 'html5') {
|
|
try {
|
|
window.document.removeEventListener('dragenter', this._onDragenter, false);
|
|
window.document.removeEventListener('dragleave', this._onDragleave, false);
|
|
this.dropElement.removeEventListener('dragover', this._onDragover, false);
|
|
this.dropElement.removeEventListener('drop', this._onDrop, false);
|
|
} catch (e) {
|
|
}
|
|
}
|
|
|
|
if (!value) {
|
|
this.dropElement = false;
|
|
return;
|
|
}
|
|
|
|
if (typeof value == 'string') {
|
|
this.dropElement = document.querySelector(value) || this.$root.$el.querySelector(value);
|
|
} else if (typeof value == 'boolean') {
|
|
this.dropElement = this.$parent.$el;
|
|
} else {
|
|
this.dropElement = this.drop;
|
|
}
|
|
if (this.dropElement && this.$mode === 'html5') {
|
|
window.document.addEventListener('dragenter', this._onDragenter, false);
|
|
window.document.addEventListener('dragleave', this._onDragleave, false);
|
|
this.dropElement.addEventListener('dragover', this._onDragover, false);
|
|
this.dropElement.addEventListener('drop', this._onDrop, false);
|
|
}
|
|
},
|
|
_onDragenter(e) {
|
|
this._dropActive++;
|
|
this.dropActive = !!this._dropActive;
|
|
e.preventDefault();
|
|
},
|
|
_onDragleave(e) {
|
|
e.preventDefault();
|
|
this._dropActive--;
|
|
if (e.target.nodeName == 'HTML' || (e.screenX == 0 && e.screenY == 0)) {
|
|
this.dropActive = !!this._dropActive;
|
|
}
|
|
},
|
|
_onDragover(e) {
|
|
e.preventDefault();
|
|
},
|
|
|
|
_onDrop(e) {
|
|
this._dropActive = 0;
|
|
this.dropActive = false;
|
|
e.preventDefault();
|
|
if (e.dataTransfer.files.length) {
|
|
for (var i = 0; i < e.dataTransfer.files.length; i++) {
|
|
var file = e.dataTransfer.files[i];
|
|
var id = createId();
|
|
var value = {id: id, size:file.size, name: file.name, progress: '0.00', speed: 0, active: false, error: '', errno: '', success: false, data: {}, request: {headers:{}, data:{}}};
|
|
this._files[id] = {file: file};
|
|
|
|
var len;
|
|
if (this.multiple) {
|
|
len = this.files.push(value);
|
|
} else {
|
|
this.files = [value];
|
|
len = 1;
|
|
}
|
|
this._files[id]._file = this.files[len-1];
|
|
this.$dispatch('addFileUpload', this.files[len-1], this);
|
|
}
|
|
}
|
|
},
|
|
|
|
_addFileUploads(el) {
|
|
var Component = this.$options.components.inputFile;
|
|
new Component({
|
|
parent: this,
|
|
el: el,
|
|
});
|
|
this.uploaded = false;
|
|
|
|
|
|
if (el.files) {
|
|
for (var i = 0; i < el.files.length; i++) {
|
|
var file = el.files[i];
|
|
var id = createId();
|
|
var value = {id: id, size:file.size, name: file.name, progress: '0.00', speed: 0, active: false, error: '', errno: '', success: false, data: {}, request: {headers:{}, data:{}}};
|
|
this._files[id] = {el:el, file: file};
|
|
|
|
var len;
|
|
if (this.multiple) {
|
|
len = this.files.push(value);
|
|
} else {
|
|
this.files = [value];
|
|
len = 1;
|
|
}
|
|
this._files[id]._file = this.files[len-1];
|
|
this.$dispatch('addFileUpload', this.files[len-1], this);
|
|
}
|
|
} else {
|
|
var id = createId();
|
|
var value = {id: id, size: -1, name: el.value.replace(/^.*?([^\/\\\r\n]+)$/, '$1'), progress: '0.00', speed:0, active: false, error: '', errno: '', success: false, data: {}, request: {headers:{}, data:{}}};
|
|
this._files[id] = {el:el};
|
|
var len;
|
|
if (this.multiple) {
|
|
len = this.files.push(value);
|
|
} else {
|
|
this.files = [value];
|
|
len = 1;
|
|
}
|
|
var len = this.files.push(file);
|
|
this._files[id]._file = this.files[len-1];
|
|
this.$dispatch('addFileUpload', this.files[len-1], this);
|
|
}
|
|
},
|
|
|
|
_fileUploads() {
|
|
if (!this.active) {
|
|
return;
|
|
}
|
|
for (; this._index < this.files.length; this._index++) {
|
|
var file = this.files[this._index];
|
|
if (file.active || file.success || file.errno) {
|
|
continue;
|
|
}
|
|
if (this.size && this.size > 0 && file.size >= 0 && file.size > this.size) {
|
|
file.error = 'Size';
|
|
file.errno = 'size';
|
|
continue;
|
|
}
|
|
|
|
if (this.extensions.length || typeof this.extensions.length == 'undefined') {
|
|
var extensions = this.extensions;
|
|
if (typeof extensions == 'object' && RegExp instanceof extensions) {
|
|
|
|
} else {
|
|
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) {
|
|
file.error = 'Extension';
|
|
file.errno = 'extension';
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (this.$mode == 'html5') {
|
|
if (this.putAction || file.putAction) {
|
|
this._fileUploadPut(file);
|
|
} else if (this.postAction || file.postAction) {
|
|
this._fileUploadHtml5(file);
|
|
} else {
|
|
file.error = 'Not Support';
|
|
file.errno = 'not_support';
|
|
continue;
|
|
}
|
|
} else {
|
|
if (this.postAction || file.postAction) {
|
|
this._fileUploadHtml4(file);
|
|
} else {
|
|
file.error = 'Not Support';
|
|
file.errno = 'not_support';
|
|
continue;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
this.active = false;
|
|
this.uploaded = true;
|
|
},
|
|
|
|
_fileUploadXhr(xhr, file, data) {
|
|
var _self = this;
|
|
var file2 = this._files[file.id];
|
|
var fileUploads = false;
|
|
var speedTime = 0;
|
|
var speedLoaded = 0;
|
|
xhr.upload.onprogress = function(e) {
|
|
if (file.removed) {
|
|
xhr.abort();
|
|
return;
|
|
}
|
|
if (!_self.active || !file.active) {
|
|
xhr.abort();
|
|
return;
|
|
}
|
|
if (e.lengthComputable) {
|
|
file.progress = (e.loaded / e.total * 100).toFixed(2);
|
|
var speedTime2 = Math.round(Date.now() / 1000);
|
|
if (speedTime2 != speedTime) {
|
|
file.speed = e.loaded - speedLoaded;
|
|
speedLoaded = e.loaded;
|
|
speedTime = speedTime2;
|
|
}
|
|
}
|
|
_self.$dispatch('fileUploadProgress', file, _self);
|
|
};
|
|
|
|
|
|
var callback = function(e) {
|
|
switch (e.type) {
|
|
case 'timeout':
|
|
file.errno = 'timeout';
|
|
file.error = 'Timeout';
|
|
break;
|
|
case 'abort':
|
|
file.errno = 'abort';
|
|
file.error = 'Abort';
|
|
break;
|
|
case 'error':
|
|
if (!xhr.status) {
|
|
file.errno = 'network';
|
|
file.error = 'Network';
|
|
} else if(xhr.status >= 500) {
|
|
file.errno = 'server';
|
|
file.error = 'Server';
|
|
} else if (xhr.status >= 400) {
|
|
file.errno = 'denied';
|
|
file.error = 'Denied';
|
|
}
|
|
break;
|
|
default:
|
|
if(xhr.status >= 500) {
|
|
file.errno = 'server';
|
|
file.error = 'Server';
|
|
} else if (xhr.status >= 400) {
|
|
file.errno = 'denied';
|
|
file.error = 'Denied';
|
|
} else {
|
|
file.progress = '100.00';
|
|
file.success = true;
|
|
}
|
|
}
|
|
file.active = false;
|
|
if (xhr.responseText) {
|
|
var contentType = xhr.getResponseHeader('Content-Type');
|
|
if (contentType && contentType.indexOf('/json') != -1) {
|
|
file.data = JSON.parse(xhr.responseText);
|
|
} else {
|
|
file.data = xhr.responseText;
|
|
}
|
|
}
|
|
if (!fileUploads) {
|
|
fileUploads = true;
|
|
if (!file.removed) {
|
|
_self.$dispatch('afterFileUpload', file, _self);
|
|
}
|
|
setTimeout(function() {
|
|
_self._fileUploads();
|
|
}, 50);
|
|
}
|
|
};
|
|
|
|
xhr.onload = callback;
|
|
xhr.onerror = callback;
|
|
xhr.onabort = callback;
|
|
xhr.ontimeout = callback;
|
|
|
|
|
|
if (this.timeout) {
|
|
xhr.timeout = this.timeout;
|
|
}
|
|
|
|
|
|
xhr.onload = callback;
|
|
xhr.onerror = callback;
|
|
xhr.onabort = callback;
|
|
xhr.ontimeout = callback;
|
|
|
|
if (this.timeout) {
|
|
xhr.timeout = this.timeout;
|
|
}
|
|
|
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
|
for (var key in this.request.headers) {
|
|
xhr.setRequestHeader(key, this.request.headers[key]);
|
|
}
|
|
for (var key in file.request.headers) {
|
|
xhr.setRequestHeader(key, file.request.headers[key]);
|
|
}
|
|
|
|
|
|
xhr.send(data);
|
|
file.active = true;
|
|
file2.xhr = xhr;
|
|
var interval = setInterval(function() {
|
|
if (!_self.active || !file.active || file.success || file.errno) {
|
|
clearInterval(interval);
|
|
if (!file.success && !file.errno) {
|
|
xhr.abort();
|
|
}
|
|
}
|
|
}, 100);
|
|
this.$dispatch('beforeFileUpload', file, this);
|
|
},
|
|
_fileUploadPut(file) {
|
|
var _self = this;
|
|
var querys = Vue.util.extend(Vue.util.extend({}, this.request.data), file.request.data);
|
|
var queryArray = [];
|
|
for (let key in querys) {
|
|
if (querys[key] !== null && typeof querys[key] !== 'undefined') {
|
|
queryArray.push(encodeURIComponent(key) + '=' + encodeURIComponent(querys[key]));
|
|
}
|
|
}
|
|
var queryString = queryArray.length ? '?' + queryArray.join('&') : '';
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open('PUT', (file.putAction || this.putAction) + queryString);
|
|
this._fileUploadXhr(xhr, file, this._files[file.id].file);
|
|
},
|
|
|
|
|
|
|
|
_fileUploadHtml5(file) {
|
|
var form = new window.FormData();
|
|
form.append(this.name, this._files[file.id].file);
|
|
for (var key in this.request.data) {
|
|
form.append(key, this.request.data[key]);
|
|
}
|
|
|
|
for (var key in file.request.data) {
|
|
form.append(key, file.request.data[key]);
|
|
}
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open('POST', file.postAction || this.postAction);
|
|
this._fileUploadXhr(xhr, file, form);
|
|
},
|
|
|
|
_fileUploadHtml4(file) {
|
|
var _self = this;
|
|
var file2 = this._files[file.id];
|
|
|
|
var fileUploads = false;
|
|
|
|
|
|
|
|
var keydown = function(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.style.width = '1px';
|
|
iframe.style.height = '1px';
|
|
iframe.style.top = '-9999px';
|
|
iframe.style.left = '-9999px';
|
|
iframe.style.position = 'absolute';
|
|
iframe.style.marginTop = '-9999em';
|
|
|
|
|
|
var form = document.createElement('form');
|
|
form.action = file.postAction || this.postAction;
|
|
form.name = 'upload-form-' + file.id;
|
|
form.setAttribute('method', 'POST');
|
|
form.setAttribute('target', 'upload-iframe-' + file.id);
|
|
form.setAttribute('enctype', 'multipart/form-data');
|
|
form.appendChild(file2.el);
|
|
for (var key in this.request.data) {
|
|
var input = document.createElement('input');
|
|
input.type = 'hidden';
|
|
input.name = key;
|
|
input.value = this.request.data[key];
|
|
form.appendChild(input);
|
|
}
|
|
|
|
for (var key in file.request.data) {
|
|
var input = document.createElement('input');
|
|
input.type = 'hidden';
|
|
input.name = key;
|
|
input.value = file.request.data[key];
|
|
form.appendChild(input);
|
|
}
|
|
|
|
var getDocumentData = function() {
|
|
var doc;
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
var callback = function(e) {
|
|
switch (e.type) {
|
|
case 'abort':
|
|
file.errno = 'abort';
|
|
file.error = 'Abort';
|
|
break;
|
|
case 'error':
|
|
var data = getDocumentData();
|
|
if (file.errno) {
|
|
|
|
} else if (data === null) {
|
|
file.errno = 'network';
|
|
file.error = 'Network';
|
|
} else {
|
|
file.errno = 'denied';
|
|
file.error = 'Denied';
|
|
}
|
|
break;
|
|
default:
|
|
var data = getDocumentData();
|
|
if (file.errno) {
|
|
|
|
} else if (data === null) {
|
|
file.errno = 'network';
|
|
file.error = 'Network';
|
|
} else {
|
|
file.progress = '100.00';
|
|
file.success = true;
|
|
}
|
|
}
|
|
|
|
file.active = false;
|
|
if (typeof data != "undefined") {
|
|
if (data && data.substr(0, 1) == '{' && data.substr(data.length - 1, 1) == '}') {
|
|
try {
|
|
data = JSON.parse(data);
|
|
} catch (err) {
|
|
}
|
|
}
|
|
file.data = data;
|
|
}
|
|
if (!fileUploads) {
|
|
document.body.removeEventListener('keydown', keydown);
|
|
document.body.removeEventListener('keydown', keydown);
|
|
fileUploads = true;
|
|
iframe.parentNode && iframe.parentNode.removeChild(iframe);
|
|
if (!file.removed) {
|
|
_self.$dispatch('afterFileUpload', file, _self);
|
|
}
|
|
setTimeout(function() {
|
|
_self._fileUploads();
|
|
}, 50);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
setTimeout(function() {
|
|
document.body.appendChild(iframe).appendChild(form).submit();
|
|
iframe.onload = callback;
|
|
iframe.onerror = callback;
|
|
iframe.onabort = callback;
|
|
|
|
file.active = true;
|
|
|
|
file2.iframe = iframe;
|
|
|
|
document.body.addEventListener('keydown', keydown);
|
|
var interval = setInterval(function() {
|
|
if (!_self.active || !file.active || file.success || file.errno) {
|
|
clearInterval(interval);
|
|
if (!file.success && !file.errno) {
|
|
iframe.onabort({type:'abort'});
|
|
}
|
|
}
|
|
}, 50);
|
|
_self.$dispatch('beforeFileUpload', file, this);
|
|
}, 10);
|
|
|
|
},
|
|
}
|
|
}
|
|
</script>
|