文件上传之解决篇

集腋成裘,聚沙成塔


说明

通过对github上fileup插件的改写,成功实现了插件的文件批量上传,删除文件,清空文件等功能。

内容

插件原来只提供了文件批量上传的功能,我通过对其源码的研究添加了文件删除,文件清空的功能,完美符合我的使用要求。

源代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// FileUp v1.0.2
// more on http://github.com/haggen/fileup
;(function(root) {

'use strict';

var FileUp, each, proxy;

proxy = function(that, fn) {
return function() {
fn.apply(that, arguments);
};
};

each = function(iterable, action) {
for(var index in iterable) {
if(iterable.hasOwnProperty(index)) {
action(index, iterable[index]);
}
}
};

FileUp = function(options) {
this.options = FileUp.options;

each(options, proxy(this, function(key, value) {
this.options[key] = value;
}));

if(this.options.endpoint === '') {
throw new Error('Endpoint cannot be empty');
}

this.listeners = {};
this.working = 0;
this.queue = [];
this.items = [];
};

FileUp.options = {

// Limit of simultaneously uploads
threshold: 1,

// Upload enpoint
endpoint: '',

// Global headers
headers: {},

// Global additional parameters
params: {},

// File field name
field: 'file',
};

FileUp.prototype = {
upload: function(index) {
var item = this.items[index];

item.xhr.addEventListener('load', proxy(this, function(e) {
item.status = 'done';
this.emit('success', item, e);
}));

item.xhr.addEventListener('error', proxy(this, function(e) {
item.status = 'failed';
this.emit('error', item, e);
}));

item.xhr.addEventListener('abort', proxy(this, function(e) {
item.status = 'aborted';
this.emit('abort', item, e);
}));

item.xhr.addEventListener('loadend', proxy(this, function(e) {
this.emit('done', item, e);
this.working -= 1;
this.work();
}));

item.xhr.upload.addEventListener('progress', proxy(this, function(e) {
this.emit('progress', item, e);
}));

item.xhr.open('POST', this.options.endpoint, true);

each(this.options.headers, proxy(this, function(key, value) {
item.xhr.setRequestHeader(key, value);
}));

each(this.options.params, proxy(this, function(key, value) {
item.data.append(key, value);
}));

item.data.append(this.options.field, item.file);

item.xhr.send(item.data);

item.status = 'uploading';
this.emit('upload', item);
},

on: function(event, callback) {
this.listeners[event] = this.listeners[event] || [];
this.listeners[event].push(callback);
},

emit: function() {
var event, args;

args = [].slice.call(arguments);
event = args.shift();

each(this.listeners[event], proxy(this, function(index, callback) {
callback.apply(this, args);
}));
},

work: function() {
while(this.queue.length > 0 && this.working < this.options.threshold) {
this.process(this.queue.shift());
}
},

process: function(index) {
this.working += 1;
this.upload(index);
},

add: function(file) {
var item = {
file: file,
status: 'enqueued',
index: this.items.length,
xhr: new XMLHttpRequest(),
data: new FormData(),
};

this.items.push(item);
this.emit('add', item);
this.queue.push(item.index);
}
};

root.FileUp = FileUp;

})(window);

很明显可以看出这是一个jquery的插件。

插件初始化传入一个option对象,对象中参数为:

  • threshold:number-可选(默认1)-文件上传数目限制
  • endpoint:String-必须-文件上传地址,必须配置
  • headers:Object-可选-额外的请求头对象
  • params:Object-可选-额外的请求参数对象
  • field:String-可选(默认file)-文件上传name标识

插件包含的监听事件:

  • add:添加文件事件(回调函数的参数item)
  • upload:文件上传开始事件(回调函数的参数item)
  • success:文件上传成功事件(回调函数的参数item,event)
  • error:文件上传失败事件(回调函数的参数item,event)
  • abort:终止文件上传事件(回调函数的参数item,event)
  • progress:文件上传过程事件(回调函数的参数item,event)
  • done:文件上传结束事件(回调函数的参数item,event)

插件包含的函数:

  • add:参数(File file)-将文件对象添加到队列中
  • on:参数(String event,Function callback)-给文件上传对象添加事件监听
  • work:参数(无)-开始讲队列中的文件上传

对插件的改动

我在对插件进行了一些的扩展,完善了插件的文件队列清空和根据文件名将文件从队列中删除的方法。
首先是清空文件列表比较简单:

1
2
3
4
5
resetList: function() {
this.working = 0;
this.queue = [];
this.items = [];
},

清空文件列表只要将顺序队列,文件队列,序号全部初始化即可。
接下来就是根据文件名称删除其中一个文件,这个一开始实现起来稍微有点问题,后台换了一个思路,首先将队列清空,然后将非删除的文件再一个一个的的添加进去即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
deleteFile: function(filename) {
var oldItems = this.items;
var newQueue = [];
var newItems = [];
oldItems.forEach(function(it, i) {
var index = newItems.length;
if(it.file.name != filename) {
var item = {
file: it.file,
status: 'enqueued',
index: index,
xhr: new XMLHttpRequest(),
data: new FormData()
};

newItems.push(item);
newQueue.push(item.index);
}
});
this.items = newItems;
this.queue = newQueue;
this.working = this.working - 1;
}

首先创建两个新队列,遍历将原队列中文件名不等于删除文件的文件名的文件添加进去,最后将新队列添赋值给原队列,将working值减一就可以了。

结语

至此,文件上传插件完成,分享到github上,地址为:
fileupload–lfy55