Android浏览器Blob问题

如果你使用canvas导出图片,并将图片存储在uint8Array中,然后想通过blob构造之后上传到诸如aliyun oss服务器,或者个人文件服务器,那么你要注意了。在chrome早先版本和目前的android中,至少是andrid5.1之前的浏览器,包括微信浏览器等,都不支持blob的构造方法,需要使用BlobBuilder。

Android浏览器Blob问题

原因

var jpeg = new Blob( [array.buffer], {type : "image/jpeg"});

这个blob的构造方法,在ios手机浏览器是支持的,但是在android手机浏览器不行。

桌面版的chrome浏览器解决了这个blob bug, 但是android手机还是有这个问题,会返回一个type error,因为android浏览器不支持这个构造方法。你必须使用老版本的BlobBuilder API.
解决方法如下:
var array = new Int8Array([17, -45.3]);
try{
var jpeg = new Blob( [array], {type : "image/jpeg"});
}catch(e){
// TypeError old chrome and FF
window.BlobBuilder = window.BlobBuilder ||
    window.WebKitBlobBuilder ||
    window.MozBlobBuilder || . window.MSBlobBuilder;
if(e.name == 'TypeError' && window.BlobBuilder){
  var bb = new BlobBuilder();
  bb.append([array.buffer]);
  var jpeg = bb.getBlob("image/jpeg");
}else if(e.name == "InvalidStateError"){
  // InvalidStateError (tested on FF13 WinXP)
  var jpeg = new Blob( [array.buffer], {type : "image/jpeg"});
}else{
// We're screwed, blob constructor unsupported entirely
}
}
但是,这里还没有结束,仅仅是开始。
报错是没有了,但是呢,发现服务端并没有你需要的文件,是空的,怎么回事呢?抓包看看
------WebKitFormBoundarysToAVAYMLPFfJF96 Content-Disposition: form-data; name="img"; filename="blob" Content-Type: application/octet-stream ------WebKitFormBoundarysToAVAYMLPFfJF96--

文件没了

发现提交的表单中没有文件内容,我们不能正常采用formdata提交了,所以需要自己封装。这里参考:
http://www.alloyteam.com/2015/04/ru-he-zai-yi-dong-web-shang-shang-chuan-wen-jian/
封装代码如下:

function FormDataShim () {

var o = this,

parts = [],// Data to be sent

boundary = Array(5).join('-') + (+new Date() * (1e16*Math.random())).toString(32),

oldSend = XMLHttpRequest.prototype.send;

this.append = function (name, value, filename) {

parts.push('--' + boundary + '/r/nContent-Disposition: form-data; name="' + name + '"');

if (value instanceof Blob) {

parts.push('; filename="'+ (filename || 'blob') +'"/r/nContent-Type: ' + value.type + '/r/n/r/n');

parts.push(value);

} else {

parts.push('/r/n/r/n' + value);

}parts.push('/r/n');};//把xhr的send方法重写一下.

XMLHttpRequest.prototype.send = function (val) {

var fr,data,oXHR = this;

if (val === o) {

// 最后加一下boundary..注意这里一定要在最后加/r/n..否则服务器有可能会解析参数失败..

parts.push('--' + boundary + '--/r/n');

data = new XBlob(parts);

fr = new FileReader();

fr.onload = function () { oldSend.call(oXHR, fr.result); };

fr.onerror = function (err) { throw err; };

fr.readAsArrayBuffer(data);

// 设置content-type

this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);

XMLHttpRequest.prototype.send = oldSend;

}else {

oldSend.call(this, val);

}};}

最后代码

下面代码仅供参考:

function newBlob(data, datatype){

var out;

try {
out = new Blob([data], {type: datatype});
}
catch (e) {
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;

if (e.name == 'TypeError' && window.BlobBuilder) {
    var bb = new BlobBuilder();
    bb.append(data.buffer);
    out = bb.getBlob(datatype);
}
else if (e.name == "InvalidStateError") {
    out = new Blob([data], {type: datatype});
}
else {}
}
return out;
}

// 判断是否需要blobbuilder
var needsFormDataShim = (function () {
var bCheck = ~navigator.userAgent.indexOf('Android')
&& ~navigator.vendor.indexOf('Google')
&& !~navigator.userAgent.indexOf('Chrome');

return bCheck && navigator.userAgent.match(/AppleWebKit//(/d+)/).pop() <= 534;
})(),
blobConstruct = !!(function () {
    try { return new Blob(); } catch (e) {}
})(),
XBlob = blobConstruct ? window.Blob : function (parts, opts) {
    var bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder);
    parts.forEach(function (p) {
    bb.append(p);
    });
    return bb.getBlob(opts ? opts.type : undefined);
};

function FormDataShim () {
// Store a reference to this
var o = this,
parts = [],// Data to be sent
boundary = Array(5).join('-') + (+new Date() * (1e16*Math.random())).toString(32),
oldSend = XMLHttpRequest.prototype.send;

this.append = function (name, value, filename) {
parts.push('--' + boundary + '/r/nContent-Disposition: form-data; name="' + name + '"');

if (value instanceof Blob) {
parts.push('; filename="'+ (filename || 'blob') +'"/r/nContent-Type: ' + value.type + '/r/n/r/n');
parts.push(value);
} else {
parts.push('/r/n/r/n' + value);
}
parts.push('/r/n');
};

// Override XHR send()
XMLHttpRequest.prototype.send = function (val) {
var fr,
data,
oXHR = this;

if (val === o) {
//注意不能漏最后的/r/n ,否则有可能服务器解析不到参数.
parts.push('--' + boundary + '--/r/n');
data = new XBlob(parts);
fr = new FileReader();
fr.onload = function () { oldSend.call(oXHR, fr.result); };
fr.onerror = function (err) { throw err; };
fr.readAsArrayBuffer(data);

this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
XMLHttpRequest.prototype.send = oldSend;
}
else {
oldSend.call(this, val);
}
};
}

//把图片转成formdata 可以使用的数据...
//这里要把/s替换掉..要不然atob的时候会出错....
function dataURLtoBlob(data) {
var tmp = data.split(',');

tmp[1] = tmp[1].replace(//s/g,'');
var binary = atob(tmp[1]);
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new newBlob(new Uint8Array(array), 'image/jpeg');
}

function uploadFile(img){
var fd = needsFormDataShim ? new FormDataShim() : new FormData();
var file = dataURLtoBlob(img);
fd.append('img',file);

var prog = function(e){
/*你的逻辑*/
}
var load = function(e){
/*你的逻辑*/
}
var error = function(e){
/*你的逻辑*/
}
var abort = function(e){
/*你的逻辑*/
}

var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress',prog,false);
xhr.addEventListener('load',load,false);
xhr.addEventListener('error',error,false);
xhr.addEventListener('abort',abort,false);

xhr.onreadystatechange = function(){
/*你的逻辑*/
}
xhr.open('POST','/upload',true);
xhr.send(fd);
}

我的代码

document.addEventListener('DOMContentLoaded', init, false);

function init() {
var u = new UploadPic();
u.init({
input: document.querySelector('#selectFile')
});
}

function UploadPic() {
this.sw = 0;
this.sh = 0;
this.tw = 0;
this.th = 0;
this.scale = 0;
this.maxSize = 0;
this.fileSize = 0;
this.fileDate = null;
this.fileType = '';
this.fileName = '';
this.input = null;
this.canvas = null;
this.mime = {};
this.type = '';
this.callback = function () {
};
this.loading = function () {
};
}

UploadPic.prototype.init = function (options) {
this.input = options.input;
this.mime = {'png': 'image/png', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'bmp': 'image/bmp'};
this.callback = options.callback || function () {
};
this._addEvent();
};

UploadPic.prototype._addEvent = function () {
var _this = this;

function tmpSelectFile(ev) {
_this._handelSelectFile(ev);
}

this.input.addEventListener('change', tmpSelectFile, false);
};

UploadPic.prototype._handelSelectFile = function (ev) {
var file = ev.target.files[0];

this.type = file.type;

// 如果没有文件类型,则通过后缀名判断(解决微信及360浏览器无法获取图片类型问题)
if (!this.type) {
this.type = this.mime[file.name.match(//.([^/.]+)$/i)[1]];
}

if (!/image.(png|jpg|jpeg|bmp)/.test(this.type)) {
alert('选择的文件类型不是图片');
return;
}

if (file.size > this.maxSize) {
alert('选择文件大于' + this.maxSize / 1024 / 1024 + 'M,请重新选择');
return;
}

this.fileName = file.name;
this.fileSize = file.size;
this.fileType = this.type;
this.fileDate = file.lastModifiedDate;

this._readImage(file);
};

UploadPic.prototype._readImage = function (file) {
var _this = this;
this._getURI(file, this.callback);
};

UploadPic.prototype._getURI = function (file, callback) {
var reader = new FileReader();
var _this = this;

function tmpLoad() {
// 头不带图片格式,需填写格式
var re = /^data:base64,/;
var ret = this.result + '';

if (re.test(ret)) ret = ret.replace(re, 'data:' + _this.mime[_this.fileType] + ';base64,');

callback && callback(ret);
}

reader.onload = tmpLoad;

reader.readAsDataURL(file);

return false;
};

 

 

谢谢!

转载请注明出处://fed123.oss-ap-southeast-2.aliyuncs.com/2016/01/14/2016_android_blob/
欢迎关注皓眸学问公众号(扫描左侧二维码),每天好文、新技术!任何学习疑问或者工作问题都可以给我留言、互动。T_T 皓眸大前端开发学习 T_T

知识共享署名 4.0 国际许可协议,转载请保留出处:前端123学堂 » Android浏览器Blob问题

赞 (0) 打赏

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

如果对您有帮助,别忘了打赏一下宝宝哦!

支付宝扫一扫打赏

微信扫一扫打赏