这几天在工作上有个功能点是用户上传图片,后端再对图片进行保存。前端用的框架是Angular4,后端是JAVA的SSM。因为Angular的fileupload和业务场景有些相悖,并且一时也没有找到其他的更好的框架来实现。所以打算自己用原生来写,代码如下:
html code
fileResolver(files: File[]) {
/** 创建临时性集合 */
const _pic = new Array();
/** 遍历传入的所有文件并解析 */
for (let index = 0; index < files.length; index++) {
const element = files[index];
console.log(element);
/** 初始化文件读取器 */
const reader = new FileReader();
/** 转换成数据流 */
reader.readAsDataURL(element);
/** 成功读取回调方法 */
reader.onload = function (theFile) {
/** 暂存至函数域数组 */
_pic.push(this.result);
};
}
return _pic;
}
console code
等到输出的时候,发现是一串Base64字符。
data:image/png;base64,****
后端接收到了,当然是把这串编码转换成文件。于是后端执行以下代码:
理所应当,觉得搞定收工。结果找到生成的文件一打开,提示文件损坏。一脸懵逼,没道理啊,一切都是按照常理来做的。接收到的base64字符串还放在了Base64转图片工具校验过,图片能正常显示啊!
后来搜索得到结果,在后端保存得去掉base64头部。也就是data:image/png;base64,头部只是告诉别人,他是个base64字符串而已,校验工具在校验的时候会自动截断它。好,知道这个原因之后。我们先去掉它,
java code
.replace("data:image/png;base64,", "")
这下应该没问题了,让我看看。还是不行,图片依然出不来,这就奇怪了。让我debug试试。打开长文本校验工具,对比发出请求的数据和接收的数据,发现base64字符串里面所有的加号全部变成了空格。查阅百度,都说用js自带的encodingURLComponent。但是我发现这个方法只适用于把请求参数直接拼接到url上发出请求,由于get请求有请求提大小限制。我的请求都是由post完成的,所以这显然不符合我的需求。
后来想了想,http发出请求时,参数会被先编码一次,后端接收到后再进行解码。那么显然,参数里面的加号是被当作连接符被转义掉了,这就相当于我们在百度输入关键词搜索,当我们输入空格的时候,会被当作拼接符来处理。知道这个就好办。我们搜索到加号的转义符是%2b。由于我前端发出请求的方式是人为拼接json,所以我在得到的json串将所有的加号全部替换成%2b:
html code
str.replace(/\+/g, '%2b');
这里用到了正则表达式,/g也就是全局替换的意思。
替换完成之后,将后端接收到的去掉了头部的base64字符串拿去校验,能正常显示。测试下后端能否转换成成功,这次图片再也没提示损坏,所以觉得事情就这么简单结束了吗?不,是我想太多了。图片虽然可以打开,但却是黑漆漆的一片,什么都没显示,于是继续找原因。
这时候我盯着那堆%2b产生了怀疑,觉得此事并不简单:莫非是这个原因?于是,我又对该字符串进行全部替换:
java code
.replaceAll("%2b", "+")
最后,终于看到了想要的结果,图片正常显示,到此为止,问题解决。
此外,有人说,根本不需要再前端将加号换成%2b,然后再到后端将%2b换成加号,直接到后端将空格换成加号一步就搞定。提出这个解决办法的人,我觉得是没有考虑仔细的。因为我在我的base64字符串里面发现,如果你有连续多个加号,它只会被替换成一个空格,具体是什么原因,我就不是特别明白了。但替换两次,是一定没有问题的。