浏览器 extension 插件开发系列(15) -- chrome多文件上传(拖拽上传或者点击上传)

前言

在这个插件的Chrome插件版本,是允许多文件上传的,包括拖拽上传和点击上传。(FirefoxSafari 不支持)。截图如下:

png

png

png

上传

popup 页面的部分代码如下 popup.js:

1
<input id="input_file" multiple="multiple" class="file-upload" type="file" name="file">

1
2
3
4
5
// 文件上传
"change #input_file": "fileInputChangeHandle",
"dragenter #input_file": "fileDragEnterHandle",
"dragleave #input_file": "fileDragLeaveHandle",
"drop #input_file": "fileDragLeaveHandle",
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
// 文件上传框改变事件, 包括拖拽上传(暂不支持文件夹上传)
fileInputChangeHandle: function (e) {
var self = this;
var did = self._dom.selectDevicePage.selectDeviceButton.attr("data-did");
// 可以多选,所以要遍历
_.each($(e.currentTarget)[0].files, function(inputFile){
var itemDom = $(new Tpl('push_msg/file_item').render());
var itemProgress = itemDom.find(".progress");
var itemBar = itemDom.find(".progress-bar");
var itemSpan = itemDom.find(".progress span");
$(".upload-list").append(itemDom);
var file = new self.Airdroid.File(inputFile);
// 初始化上传条目
// 开始上传
file.upload(did)
.progress(function (percentComplete) {
itemBar.css('width', percentComplete + '%');
itemSpan.text(percentComplete + '%');
})
.done(function () {
itemSpan.text(file.name + '上传成功');
itemProgress.removeClass("progress-striped");
//$.toast('Push Successly', 'success');
})
.always(function () {
});
});
},
// 拖拽移进去
fileDragEnterHandle: function(){
$("#tab_pane_file").addClass("drag");
},
// 拖拽移进去
fileDragLeaveHandle: function(){
$("#tab_pane_file").removeClass("drag");
},

而最主要的上传逻辑在 file.js 这个 model 上:

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
// 文件上传操作
(function () {
var File = function (file) {
this.file = file;
this.name = file.name;
this.size = file.size;
};

File.prototype = {
upload: function (deviceId) {
var self = this,
file = this.file, uploadUrl,tokenObj;
var deviceObj = Airdroid.Account.getDeviceObjById(deviceId);
var defer = $.Deferred();
// 这边先模仿pc端的发送文件流程
Airdroid.Server.getUploadToken({
// 上传文件本地名称(filename用des加密)
filename: Airdroid.Util.Des.encrypt(file.name),
// 国籍信息(服务器根据这个字段判断是否是中国来选择存储地区)
country: Airdroid.Account.getAccountCounty(),
// 账号id
account_id: Airdroid.Account.getAccountId(),
// 文件发送端的key, 区别于pc端,即channelId
from: Airdroid.PushManage.getChannelId(),
// 发送端设备的push前缀(100,200)
from_type: Airdroid.PushManage.DEVICE_PUSH_OPT.CHROME.key,
// 文件接收端的key
to: deviceObj.getId(),
// 接收端设备的push前缀(100,200)
to_type: Airdroid.PushManage.DEVICE_PUSH_OPT.ANDROID.key,
// 文件大小(单位:byte)
size: file.size,
// 设备类型
device_type: Airdroid.PushManage.DEVICE_PUSH_OPT.CHROME.type,
// 文件hash值(md5加密)
hash: ""
}).done(function(resp){
try{
resp = JSON.parse(Airdroid.Util.Des.decrypt(resp));
switch(resp.cloud){
case "q":
// 如果是七牛
uploadUrl = "http://upload.qiniu.com";
tokenObj = {
token: resp.data.token,
key: resp.data.key
};
break;
case "a":
// 如果是亚马逊
uploadUrl = resp.data.form_action;
tokenObj = {
key: resp.data.key,
acl: resp.data.acl,
success_action_redirect: resp.data.success_action_redirect,
"X-Amz-Credential": resp.data.x_amz_credential,
"X-Amz-Algorithm": resp.data.x_amz_algorithm,
"X-Amz-Date": resp.data.x_amz_date,
Policy: resp.data.policy,
"X-Amz-Signature": resp.data.x_amz_signature
};
break;
}
// 接下来开始上传
xhrUploader(file, uploadUrl, tokenObj).done(function () {
console.log('done');
defer.resolve.apply(this, arguments);
})
.fail(function () {
console.log('fail');
defer.reject.apply(this, arguments);
})
.progress(function (percentComplete) {
defer.notify.apply(this, arguments);
console.log('uploaded:', percentComplete);
});
}catch(e){

}
});
return defer;
}
};
// 文件上传helper对象
var xhrUploader = function (file, uploadUrl, tokenObj) {
if (!file) throw Error('file is required');
var formData, k, defer, xhr;

defer = $.Deferred();
xhr = new XMLHttpRequest();

formData = new FormData();
for (k in tokenObj) {
formData.append(k, tokenObj[k]);
}
formData.append('file', file);

xhr.open("POST", uploadUrl, true);
xhr.upload.onprogress = function (e) {
var percentComplete;
if (e.lengthComputable) {
percentComplete = Math.floor((e.loaded / e.total) * 100);
defer.notify(percentComplete);
}
};
xhr.onreadystatechange = function () {
if (xhr.status === 200) {
defer.resolve.apply(this, arguments);
}
};
xhr.onerror = function () {
defer.reject.apply(this, arguments);
};
xhr.send(formData);
return defer;
};
Airdroid.File = File;
})();

这样就可以实现文件上传了。同时 manifest.json 要对 upload url 有授权:

png

总结

到现在基本上一个比较完整的包含 ChromeFirefoxSafari, 并且包含不少功能的浏览器扩展程序就完成了。当然当初在做这个的时候,遇到的问题比写在文章的肯定还要多。所以后面会针对各个浏览器遇到的问题,再做一些总结和收集。


系列文章:
浏览器 extension 插件开发系列(01) -- 前言和确认需求
浏览器 extension 插件开发系列(02) -- Chrome 插件的启动以及调试
浏览器 extension 插件开发系列(03) -- Firefox 插件的启动以及调试
浏览器 extension 插件开发系列(04) -- Safari 插件的添加以及调试
浏览器 extension 插件开发系列(05) -- Safari 插件申请开发者证书
浏览器 extension 插件开发系列(06) -- 各浏览器导航栏按钮的配置的点击出现的panel
浏览器 extension 插件开发系列(07) -- 获取各浏览器端的背景页
浏览器 extension 插件开发系列(08) -- 背景页启动和登录持久化
浏览器 extension 插件开发系列(09) -- popup以及其他前端页面的启动
浏览器 extension 插件开发系列(10) -- 事件驱动模型
浏览器 extension 插件开发系列(11) -- 登录模块(包括第三方登录和弹框)
浏览器 extension 插件开发系列(12) -- 实现右键菜单推送消息
浏览器 extension 插件开发系列(13) -- 实现消息过来出现桌面通知
浏览器 extension 插件开发系列(14) -- 点击reply出现回复小窗口
浏览器 extension 插件开发系列(15) -- chrome多文件上传(拖拽上传或者点击上传)
浏览器 extension 插件开发系列(16) -- Firefox 遇到的问题
浏览器 extension 插件开发系列(17) -- Safari 遇到的问题