陆陆续续做了不少需要上传图片的项目,场景各种各样,从前后端数据交互来看,有直接从浏览器里发ajax到后端,也有用Node做中间层,在Node端转发到后端。从用户使用方式来看,有在移动端H5页面获取客户端图片,也有在PC端直接上传图片。所以打算做一个总结,梳理不同场景的实现思路以及遇到的问题。

H5页面使用Ajax直接发送到后端

整体思路如下图所示



在H5页面调用JsBridge方法,获取图片的Base64编码,将base64编码转换成Blob二进制文件(图中没有标注出来),接下来在浏览器端构造FormData数据,最后使用Ajax发送到后端,后端返回图片的URL。

在实际项目中,base64编码可以赋值给img标签的src属性,用于预览图片(例子可查看FileReader对象 - MDN)。在发送给后端之前,将其转换成Blob二进制文件,代码如下:

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
/**
* [convertToBlob 将base64转换为二进制文件]
* @param {[type]} base64Data [bas64文件]
* @param {[type]} type [二进制文件类型,比如: 'image/jpg']
* @return {[type]} [二进制文件]
*/
function convertToBlob(base64Data, type) {
//去掉base64中的换行符,webkit会自动去除,但是IOS9以及IOS8中不会自动去除,导致转换出错
base64Data = base64Data.replace(/\s/g, '');

var text = window.atob(base64Data.split(",")[1]);
var buffer = new ArrayBuffer(text.length);
var ubuffer = new Uint8Array(buffer);
for (var i = 0; i < text.length; i++) {
ubuffer[i] = text.charCodeAt(i);
}

var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
var blob;

if (Builder) {
var builder = new Builder();
builder.append(buffer);
blob = builder.getBlob(type);
} else {
blob = new window.Blob([buffer], {
type: type
});
}
return blob;
}

PC页面使用Ajax直接发送到后端

PC和H5页面唯一一个不同点在于获取图片数据的方式不同。H5通过JsBridge方法直接获取图片的Base64编码,而PC端需要我们自己去读取。



通过input:file标签获取File对象,使用FileReader对象的readAsDataURL方法 - MDN读取File得到Base64编码,将Base64编码转换成Blob二进制文件,最后构造FormData数据。

通过上面两种场景,我们基本能总结出上传图片的思路。



如果觉得不好理解的话,建议先去MDN了解下各个数据类型(File, Blob)的含义,以及和JavaScript中原生数据类型的区别。

Node层转发

出于尝鲜的心理,在最近的一个项目中,我使用了Node层转发的方式来处理图片。沿用前面总结的思路,我们需要获取到图片内容,构造FromData数据

Node层怎么获取到图片内容呢?,Node层怎么构造FormData数据呢?



我们使用了formidable来接收浏览器端发送的FormData数据,formidable非常好的一点是将文件内容和其他参数分开了,方便我们获取

request有一个 advanced 用法是在使用post方法上传数据时,能构造FormData数据,

完整代码:

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
var form = new formidable.IncomingForm();
form.parse(this.req, function(err, fileds, files) {
// fileds:和文件一起上传的参数
// files: 文件,File类型
if(err) {
return console.log('param error', err);
}

var filePath = files[0].path;
var fileName = files[0].name;

var r = request.post('uploadImageUrl', function(err, httpResponse, body) {
if(err) {
console.log(err);
}
body = JSON.parse(body);
console.log(body) //返回数据
})
var form = r.form();
//和文件一起上传的其他参数
form.append('param', fileds.param);
//读取文件流
form.append('multipartFile', require('fs').createReadStream(filePath), {
filename: fileName
});

});

当然在Node层构造FormData还有其他方式,比如:form-data

有一个需要注意的地方,在Node层,是没法像浏览器端那样将Base64编码转换成Blob二进制文件的方法的,我们获取二进制文件内容的方式,是通过createReadStream创建一个读取的数据流。

ok,总结来看,不管是在浏览器端还是在Node层,我们的最终目的都是要获取图片的二进制文件,然后构造FormData数据,只是在不同环境中,获取二进制文件的方式不同。

希望对大家有帮助 ~