使用Greasemonkey上传文件到服务器

问题背景

在用GreaseMonkey的过程中,偶尔遇到需要上传文件到服务器的情况,而GM并不支持直接上传二进制文件(本文中专指图片文件),因为它的参数是这么规定的:

data:String Optional. Data to send in the request body. Usually for POST method requests.

也就是说,参数里的data只支持 String 类型的变量。而且从目前来看,GM似乎并没有打算支持上传二进制文件,就像原生 *XMLHttpRequest 那样,支持 FormData数据。所以,要想支持二进制文件,还得自己动手实现。 网上已经有一些GM的库,支持二进制文件,比如这一个Binary library ,不过,这个时间比较早了,有一些可改进的地方。所以,就以这个为原型,写了一个新的函数,来支持二进制文件上传。

解决方法

其实,要想让GM支持二进制文件,也不难,首先得回归到HTTP本身。 在表单中,如果有file类型的输入,即

1
<input type="file" name=""/>

我们要在form标签中加上一个enctype的属性

1
<form name="" action="" method="POST" enctype="multipart/form-data">

之所以能够提交二进制,就在于这个属性

enctype=“multipart/form-data”

有关这个属性的部分,请大家参考 HTTP协议之multipart/form-data请求分析 在了解了这些内容之后,我们就可以实现GM支持二进制文件上传了。

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
var BinaryPost = BinaryPost || (function(GM_POST){
//生成标识
var getBoundary = function(){
var z = 98729145294692; var a = 12400722650394;
return Array(28).join('-') + Math.floor(Math.random() * (z - a)) + a;
}
return function (obj) {
var body = [];
var boundary = getBoundary();
/**
可能有多个文件需要上传
因此需要等待每个文件读取完成
这里使用了ES6 中的Promise来实现
*/

Promise.all((Object.getOwnPropertyNames(obj.data)).map(function(filedName){
if(obj.data[filedName] instanceof Blob){
return new Promise(function(success){
let fr = new FileReader();
fr.onload = function(){
body.push('--' + boundary);
var disp = 'Content-Disposition: form-data; name="' + filedName + '"';
//必须添加文件名,否则服务器可能报错
disp += '; filename="' + (obj.data[filedName].name||'BinaryPost') + '"';
body.push(disp);
body.push('Content-type: ' + obj.data[filedName].type);
body.push('');
body.push(fr.result);
success();
};
fr.readAsBinaryString(obj.data[filedName]);
});
}
body.push('Content-Disposition: form-data; name="' + filedName + '"');
body.push('');
body.push(obj.data[filedName]);
return Promise.resolve(true);
})).then(function(){
body.push('--' + boundary + '--');
var data = body.join('\r\n');
var headers = {};
if(obj.headers){ headers = obj.headers; }
headers["Content-Type"] = 'multipart/form-data; boundary=' + boundary;
headers["Content-Length"] = data.length
var request = {
method: 'POST',
url: obj.url,
binary: true,
headers: headers,
data: data,
onload: obj.onload,
ontimeout: obj.ontimeout || obj.onload,
timeout: obj.timeout || 2000,
onerror:obj.onload,
onprogress: obj.onprogress || function(){}
};
GM_POST(request);
});
}
})(GM_xmlhttpRequest);

使用方法就是

BinaryPost(object)

其中object 有如下属性

url String 必须 数据要提交的页面 onload Function 必须 数据上传完成后的回调函数 timeout Number 可选 等待的毫秒数,默认2000ms timeout Function 可选 超时后执行的回调函数,如果没有,则执行onload函数 data Object 必须 要提交数据 包含以下属性

filedName Blob || File || String 必须 filedName为form表单中的字段名。这里并不支持一个字段多个文件,所以,你必须为每个文件使用一个字段。

使用方法很简单,如果你直接拷贝上面的代码,直接使用BinaryPost即可。当然,你也可以通过引用外部外部文件的方式来使用

1
//@require https://raw.githubusercontent.com/holynewbie/uploadPicToWeibo/gh-pages/BinaryPost.js

然后直接使用即可。

坚持原创技术分享,您的支持将鼓励我继续创作!