v0.2.0, Add Put method, Upload speed

1.0
git 10 years ago
parent 334ec1a2d0
commit 9aed68961e

@ -15,7 +15,7 @@
var FileUpload = require('vue-upload-component'); var FileUpload = require('vue-upload-component');
new Vue({ new Vue({
template: '<file-upload action="/"></file-upload>', template: '<file-upload post-action="/post.method" put-action="/put.method"></file-upload>',
components: { components: {
FileUpload: FileUpload FileUpload: FileUpload
} }
@ -27,7 +27,7 @@
```js ```js
import FileUpload from 'vue-upload-component' import FileUpload from 'vue-upload-component'
new Vue({ new Vue({
template: '<file-upload action="/"></file-upload>', template: '<file-upload post-action="/post.method" put-action="/put.method"></file-upload>',
components: { components: {
FileUpload FileUpload
} }
@ -94,16 +94,16 @@ npm run build
{ {
files: [ files: [
{ {
id: 'String', id: 'String', // Read only
name: 'filename String', name: 'filename String',
size: 'filesize Number', size: 'filesize Number',
progress: 'progress String', progress: 'progress String', // Read only
speed: "Speed Number", // Read only
active: 'active Boolean', active: 'active Boolean',
error: 'error String', error: 'error String',
errno: 'errno String', errno: 'errno String',
success: 'success Boolean', success: 'success Boolean', // Read only
data: 'Response data Object or String', data: 'Response data Object or String', // Read only
request: { request: {
headers: { headers: {
"X-Csrf-Token": "xxxx", "X-Csrf-Token": "xxxx",
@ -112,10 +112,10 @@ npm run build
"_csrf_token": "xxxxxx", "_csrf_token": "xxxxxx",
}, },
}, },
} }
], ],
// Global // Global
request: { request: {
headers: { headers: {
@ -125,11 +125,41 @@ npm run build
"_csrf_token": "xxxxxx", "_csrf_token": "xxxxxx",
}, },
}, },
active: false,
uploaded: true, // Read only
dropActive: false, // Read only
} }
``` ```
### Props ### Props
``` html ``` html
<file-upload :title="Add upload files" :name="file" :action="./upload.php" :accept="accept" :multiple="multiple" :size="size" :timeout="3600000"></file-upload> <file-upload :title="Add upload files" :name="file" :drop="Boolean (true = $parent) or Element or Css Selector" :extensions="Array or String or Regular" :post-action="./post.method" :put-action="./put.method" :accept="accept" :multiple="multiple" :size="size" :timeout="3600000"></file-upload>
```
```
title="Add upload files"
name="post file name"
drop="Boolean (true = $parent) or Element or Css Selector"
extensions="Array or String or Regular" :post-action="./post.method"
post-action="./post.method"
put-action="./put.method"
accept="accept"
multiple="multiple"
size="max Size"
timeout="3600000"
``` ```

1545
dist/example.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -5,12 +5,28 @@
<title>vue-upload-component</title> <title>vue-upload-component</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.24/vue.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.24/vue.min.js"></script>
<style type="text/css"> <style type="text/css">
#app {
position: relative;
}
.file-uploads-label {
display: block;
}
.file-uploads-label span{ .file-uploads-label span{
display: block;
font-size: 18px; font-size: 18px;
padding: 1em; padding: 1em;
font-weight: bold; font-weight: bold;
border: 1px solid #888; border: 1px solid #888;
} }
.drop-active {
top: 0;
bottom: 0;
right: 0;
left: 0;
position: absolute;
opacity: .4;
background: #000;
}
</style> </style>
</head> </head>
<body> <body>
@ -19,9 +35,11 @@
<thead> <thead>
<tr> <tr>
<th>Index</th> <th>Index</th>
<th>ID</th>
<th>Name</th> <th>Name</th>
<th>Size</th> <th>Size</th>
<th>Progress</th> <th>Progress</th>
<th>Speed</th>
<th>Active</th> <th>Active</th>
<th>Error</th> <th>Error</th>
<th>Errno</th> <th>Errno</th>
@ -33,9 +51,11 @@
<tbody> <tbody>
<tr v-for="file in $refs.upload.files"> <tr v-for="file in $refs.upload.files">
<td>{{$index}}</td> <td>{{$index}}</td>
<td>{{file.id}}</td>
<td>{{file.name}}</td> <td>{{file.name}}</td>
<td>{{file.size}}</td> <td>{{file.size | formatSize}}</td>
<td>{{file.progress}}</td> <td>{{file.progress}}</td>
<td>{{file.speed | formatSize}}</td>
<td>{{file.active}}</td> <td>{{file.active}}</td>
<td>{{file.error}}</td> <td>{{file.error}}</td>
<td>{{file.errno}}</td> <td>{{file.errno}}</td>
@ -51,27 +71,54 @@
<tbody> <tbody>
<tr> <tr>
<td> <td>
<file-upload title="Add upload files" name="file" action="./upload.php" :accept="accept" :multiple="multiple ? 'true' : null" :size="size" v-ref:upload></file-upload> <file-upload title="Add upload files" class="file-upload" name="file" post-action="./post.php" put-action="./put.php" :extensions="extensions" :accept="accept" :multiple="multiple ? 'true' : null" :size="size" v-ref:upload :drop="drop"></file-upload>
</td> </td>
<td> <td>
<button v-if="$refs.upload && $refs.upload.active" type="submit" @click.prevent="$refs.upload.active = !$refs.upload.active">Stop upload</button> <button v-if="$refs.upload && $refs.upload.active" type="submit" @click.prevent="$refs.upload.active = !$refs.upload.active">Stop upload</button>
<button v-else type="submit" @click.prevent="$refs.upload.active = !$refs.upload.active">Start upload</button> <button v-else type="submit" @click.prevent="$refs.upload.active = !$refs.upload.active">Start upload</button>
</td> </td>
<td> <td>
<label>
Accept: <input type="text" v-model="accept"> Accept: <input type="text" v-model="accept">
</label>
</td>
<td>
<label>
Extensions: <input type="text" v-model="extensions">
</label>
</td>
<td>
<label>
Drop: <input type="checkbox" id="checkbox" v-model="drop">
</label>
</td> </td>
<td> <td>
<label>
Max file size: <input type="text" v-model="size"> Max file size: <input type="text" v-model="size">
</label>
</td> </td>
<td> <td>
<label>
Multiple: <input type="checkbox" id="checkbox" v-model="multiple"> Multiple: <input type="checkbox" id="checkbox" v-model="multiple">
</label>
</td>
</tr>
<tr>
<td>
Active: {{$refs.upload.active}}
</td> </td>
<td> <td>
Status: Active: {{$refs.upload.active}}, Uploaded: {{$refs.upload.uploaded}} Uploaded: {{$refs.upload.uploaded}}
</td>
<td>
Drop active: {{$refs.upload.dropActive}}
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div v-show="$refs.upload.dropActive" class="drop-active">
Drop ing
</div>
</div> </div>
<script src="./dist/example.js"></script> <script src="./dist/example.js"></script>
</body> </body>

@ -1,7 +1,7 @@
{ {
"name": "vue-upload-component", "name": "vue-upload-component",
"description": "Vue.js file upload component, Support for multiple file uploads, progress, html5, html4, support ie9", "description": "Vue.js file upload component, Support for multiple file uploads, progress, html5, html4, support ie9",
"version": "0.1.11", "version": "0.2.0",
"author": "LianYue", "author": "LianYue",
"scripts": { "scripts": {
"dev": "webpack-dev-server --inline --hot", "dev": "webpack-dev-server --inline --hot",

@ -43,6 +43,11 @@
</style> </style>
<script> <script>
const createId = function() {
return Math.random().toString(36).substr(2);
}
export default { export default {
props: { props: {
title: { title: {
@ -51,14 +56,18 @@ export default {
}, },
name: { name: {
type: String, type: String,
required: true default: 'file',
},
drop: {
default: false,
}, },
id: { extensions: {
},
postAction: {
type: String, type: String,
}, },
action: { putAction: {
type: String, type: String,
required: true
}, },
accept: { accept: {
type:String, type:String,
@ -91,6 +100,8 @@ export default {
files: [], files: [],
active: false, active: false,
uploaded: true, uploaded: true,
dropActive: false,
dropElement: false,
request: { request: {
data: {}, data: {},
headers: {}, headers: {},
@ -98,6 +109,10 @@ export default {
} }
}, },
created() {
this._drop(this.drop);
},
init() { init() {
var input = document.createElement('input'); var input = document.createElement('input');
input.type = 'file'; input.type = 'file';
@ -107,15 +122,20 @@ export default {
this.$mode = 'html4'; this.$mode = 'html4';
} }
this._index = 0; this._index = 0;
this._dropActive = 0;
this._files = {}; this._files = {};
}, },
beforeDestroy() { beforeDestroy() {
this.active = false; this.active = false;
this.files = []; this.files = [];
}, },
watch: { watch: {
drop(value) {
this._drop(value);
},
files(files) { files(files) {
var ids = []; var ids = [];
for (var i = 0; i < files.length; i++) { for (var i = 0; i < files.length; i++) {
@ -162,6 +182,78 @@ export default {
}, },
methods: { methods: {
_drop(value) {
if (this.dropElement) {
try {
if (this.$mode === 'html5') {
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);
} else if (typeof value == 'boolean') {
this.dropElement = this.$parent.$el;
} else {
this.dropElement = this.drop;
}
if (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) { _addFileUploads(el) {
var Component = this.$options.components.inputFile; var Component = this.$options.components.inputFile;
new Component({ new Component({
@ -174,8 +266,8 @@ export default {
if (el.files) { if (el.files) {
for (var i = 0; i < el.files.length; i++) { for (var i = 0; i < el.files.length; i++) {
var file = el.files[i]; var file = el.files[i];
var id = 'upload-file-' + Math.random().toString(36).substr(2); var id = createId();
var value = {id: id, size:file.size, name: file.name, progress: '0.00', active: false, error: '', errno: '', success: false, data: {}, request: {headers:{}, data:{}}}; 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}; this._files[id] = {el:el, file: file};
var len; var len;
@ -189,8 +281,8 @@ export default {
this.$dispatch('addFileUpload', this.files[len-1], this); this.$dispatch('addFileUpload', this.files[len-1], this);
} }
} else { } else {
var id = 'upload-file-' + Math.random().toString(36).substr(2); var id = createId();
var value = {id: id, size: -1, name: el.value.replace(/^.*?([^\/\\\r\n]+)$/, '$1'), progress: '0.00', active: false, error: '', errno: '', success: false, data: {}, request: {headers:{}, data:{}}}; 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}; this._files[id] = {el:el};
var len; var len;
if (this.multiple) { if (this.multiple) {
@ -220,10 +312,46 @@ export default {
continue; 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.$mode == 'html5') {
if (this.putAction || file.putAction) {
this._fileUploadPut(file);
} else if (this.postAction || file.postAction) {
this._fileUploadHtml5(file); this._fileUploadHtml5(file);
} else { } else {
file.error = 'Not Support';
file.errno = 'not_support';
continue;
}
} else {
if (this.postAction || file.postAction) {
this._fileUploadHtml4(file); this._fileUploadHtml4(file);
} else {
file.error = 'Not Support';
file.errno = 'not_support';
continue;
}
} }
return; return;
} }
@ -231,24 +359,12 @@ export default {
this.uploaded = true; this.uploaded = true;
}, },
_fileUploadHtml5(file) { _fileUploadXhr(xhr, file, data) {
var _self = this; var _self = this;
var file2 = this._files[file.id]; var file2 = this._files[file.id];
var fileUploads = false; var fileUploads = false;
var speedTime = 0;
var form = new window.FormData(); var speedLoaded = 0;
form.append(this.name, file2.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.upload.onprogress = function(e) { xhr.upload.onprogress = function(e) {
if (file.removed) { if (file.removed) {
xhr.abort(); xhr.abort();
@ -260,6 +376,12 @@ export default {
} }
if (e.lengthComputable) { if (e.lengthComputable) {
file.progress = (e.loaded / e.total * 100).toFixed(2); 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); _self.$dispatch('fileUploadProgress', file, _self);
}; };
@ -329,23 +451,28 @@ export default {
xhr.timeout = this.timeout; xhr.timeout = this.timeout;
} }
xhr.open('POST', this.action);
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'); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
for (var key in this.request.headers) { for (var key in this.request.headers) {
xhr.setRequestHeader(key, this.request.headers[key]); xhr.setRequestHeader(key, this.request.headers[key]);
} }
for (var key in file.request.headers) { for (var key in file.request.headers) {
xhr.setRequestHeader(key, file.request.headers[key]); xhr.setRequestHeader(key, file.request.headers[key]);
} }
xhr.send(form); xhr.send(data);
file.active = true; file.active = true;
file2.xhr = xhr; file2.xhr = xhr;
var interval = setInterval(function() { var interval = setInterval(function() {
if (!_self.active || !file.active || file.success || file.errno) { if (!_self.active || !file.active || file.success || file.errno) {
clearInterval(interval); clearInterval(interval);
@ -353,9 +480,42 @@ export default {
xhr.abort(); xhr.abort();
} }
} }
}, 50); }, 100);
this.$dispatch('beforeFileUpload', file, this); 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) { _fileUploadHtml4(file) {
var _self = this; var _self = this;
@ -384,7 +544,7 @@ export default {
var form = document.createElement('form'); var form = document.createElement('form');
form.action = this.action; form.action = file.postAction || this.postAction;
form.name = 'upload-form-' + file.id; form.name = 'upload-form-' + file.id;
form.setAttribute('method', 'POST'); form.setAttribute('method', 'POST');
form.setAttribute('target', 'upload-iframe-' + file.id); form.setAttribute('target', 'upload-iframe-' + file.id);

@ -2,6 +2,20 @@
import FileUpload from './FileUpload.vue'; import FileUpload from './FileUpload.vue';
Vue.filter('formatSize', function(size) {
if (size > 1024 * 1024 * 1024 * 1024) {
return (size / 1024 / 1024 / 1024 / 1024).toFixed(2) + ' TB';
} else if (size > 1024 * 1024 * 1024) {
return (size / 1024 / 1024 / 1024).toFixed(2) + ' GB';
} else if (size > 1024 * 1024) {
return (size / 1024 / 1024).toFixed(2) + ' MB';
} else if (size > 1024) {
return (size / 1024).toFixed(2) + ' KB';
}
return size.toString() + ' B';
});
new Vue({ new Vue({
el:'#app', el:'#app',
components: { components: {
@ -11,6 +25,10 @@ new Vue({
accept: 'image/*', accept: 'image/*',
size: 1024 * 1024 * 10, size: 1024 * 1024 * 10,
multiple: true, multiple: true,
extensions: 'gif,jpg,png',
// extensions: ['gif','jpg','png'],
// extensions: /\.(gif|png|jpg)$/i,
drop: true,
}, },
compiled: function() { compiled: function() {
this.$refs.upload.request = { this.$refs.upload.request = {

Loading…
Cancel
Save