关于安卓手机微信浏览器中使用XMLHttpRequest 2上传图片显示字节数为0的解决办法
前端JS中使用XMLHttpRequest2上传图片到服务器,PC端和大部分手机上都正常,但在少部分安卓手机上上传失败,服务器上查看图片,显示字节数为0。下面是上传图片的核心代码:
HTML
<inputtype="file"id="choose"capture="camera"accept="image/*">
JavaScript
varfilechooser=document.getElementById("choose");
filechooser.onchange=function(){
var_this=$(this);
if(!this.files.length)return;
varfiles=Array.prototype.slice.call(this.files);
if(files.length>1){
alert("一次只能上传1张图片");
return;
}
files.forEach(function(file,i){
if(!/\/(?:jpeg|png|gif)/i.test(file.type))return;
varreader=newFileReader();
reader.onload=function(){
varresult=this.result;
upload(result,file.type);
};
reader.readAsDataURL(file);
});
};
functionupload(basestr,type){
varxhr=newXMLHttpRequest();
vartext=window.atob(basestr.split(",")[1]);
varbuffer=newUint8Array(text.length);
varpecent=0;
for(vari=0;i<text.length;i++){
buffer[i]=text.charCodeAt(i);
}
varblob=getBlob(buffer,type);
varformdata=newFormData();
formdata.append('imagefile',blob);
xhr.open('post','/uploadtest');
xhr.onreadystatechange=function(){
if(xhr.readyState==4&&xhr.status==200){
varjsonData=JSON.parse(xhr.responseText);
console.log(jsonData);
}
};
//利用progress事件显示数据发送进度
xhr.upload.addEventListener('progress',function(e){
pecent=~~(100*e.loaded/e.total)/2;
//利用pecent来显示上传进度
},false);
xhr.send(formdata);
}
functiongetBlob(buffer,format){
varBuilder=window.WebKitBlobBuilder||window.MozBlobBuilder;
if(Builder){
varbuilder=newBuilder();
builder.append(buffer);
returnbuilder.getBlob(format);
}else{
returnnewwindow.Blob([buffer],{type:format});
}
}
上述代码使用FormData来实现表单数据提交。FormData是一种针对XHR2设计的新型数据类型,使用它我们可以很方便地实时以JavaScript创建HTML<Form>,然后通过AJAX提交该表单。在上述代码中,提交的表单中的字段名为imagefile,值是blob,这是一个通过getBlob函数构造并返回的文件Blob。通过该方法上传文件简单直观。
然后我们在服务端接收并保存图片,并返回已上传的图片的信息。
下面是Node.js代码的示例:
varQ=require('q');
varfs=require('fs');
varpath=require('path');
varformidable=require('formidable');
varmoment=require('moment');varimageUpload=function(){};
imageUpload.prototype.useFormParseCallback=function(req){
vardeferred=Q.defer();
varform=newformidable.IncomingForm();
form.parse(req,deferred.makeNodeResolver());
returndeferred.promise;
};
imageUpload.prototype.uploadImageTest=function(req){
varpathName='uploadImgs/dealInfo/';
varuploadPath=path.join(__dirname,'../../public/',pathName);
returnthis.useFormParseCallback(req).then(function(files){
varfile=files[1].imagefile;
varfileType=files[1].imagefile.type.split('/')[1];
varnewFileName='upload_'+moment().format('x')+Math.random().toString().substr(2,10)+'.'+fileType;
varreadStream=fs.createReadStream(file.path);
varwriteStream=fs.createWriteStream(uploadPath+newFileName);
vardeferred=Q.defer();
readStream.pipe(writeStream);
readStream.on('end',deferred.makeNodeResolver());
returndeferred.promise.then(function(){
fs.unlinkSync(file.path);
return{
fileName:newFileName,
filePath:'/'+pathName+newFileName,
fileSize:file.size/1024>1024?(~~(10*file.size/1024/1024))/10+"MB":~~(file.size/1024)+"KB"
};
});
});
};
module.exports=imageUpload;
我们使用formidable这个包来接收上传文件的数据,然后将文件保存到/public/uploadImgs/dealInfo目录下(假定已在express中将public设置为static的根目录),并将图片按照指定的规则重命名,以保证上传图片不会因为名称相同而被覆盖。另外,代码中使用Q来避免直接使用回调函数,以更好地对函数功能进行分离。
上面的代码在PC端浏览器以及大部分主流移动设备上都能正常工作,但是少部分Android设备上却会出现上传的图片字节数为0的情况。具体的原因大家可以看下面几个网页中的描述:
就是说这个是Android的一个bug!
那如何解决呢?
其实从上面给出的页面中可以找到答案,就是我们得换一种文件上传方式。在XHR2中,除了以Blob的方式上传文件外,还可以ArrayBuffer的方式上传文件。
下面是修改之后的前端JavaScript代码:
varfilechooser=document.getElementById("choose");
filechooser.onchange=function(){
var_this=$(this);
if(!this.files.length)return;
varfiles=Array.prototype.slice.call(this.files);
if(files.length>1){
alert("一次只能上传1张图片");
return;
}
files.forEach(function(file,i){
if(!/\/(?:jpeg|png|gif)/i.test(file.type))return;
varreader=newFileReader();
reader.onload=function(){
varresult=this.result;
upload(result,file.type);
};
reader.readAsDataURL(file);
});
};
functionupload(basestr,type){
varxhr=newXMLHttpRequest();
vartext=window.atob(basestr.split(",")[1]);
varbuffer=newUint8Array(text.length);
varpecent=0;
for(vari=0;i<text.length;i++){
buffer[i]=text.charCodeAt(i);
}
xhr.open('post','/uploadtest?filetype='+type.split('/')[1]);
xhr.setRequestHeader('Content-Type','application/octet-stream');
xhr.onreadystatechange=function(){
if(xhr.readyState==4&&xhr.status==200){
varjsonData=JSON.parse(xhr.responseText);
console.log(jsonData);
}
};
//利用progress事件显示数据发送进度
xhr.upload.addEventListener('progress',function(e){
pecent=~~(100*e.loaded/e.total)/2;
//利用pecent来显示上传进度
},false);
xhr.send(buffer.buffer);//以ArrayBuffer的方式上传图片
}
我将有变化的地方加了高亮显示。以ArrayBuffer方式上传图片必须添加'application/octet-stream'的RequestHeader,否则服务器无法响应请求。另外,通过这种方式上传图片我们也无法从表单数据中获取到文件类型,可以将文件类型以query的方式传到服务器,然后服务器根据文件类型来生成对应的文件,以下是经过少量修改之后的服务器代码:
imageUpload.prototype.uploadImageTest=function(req){
varpathName='uploadImgs/dealInfo/';
varuploadPath=path.join(__dirname,'../../public/',pathName);
returnthis.useFormParseCallback(req).then(function(files){
varfile=files[1].file;
varfileType=req.query.filetype?('.'+req.query.filetype):'.png';
varnewFileName='upload_'+moment().format('x')+Math.random().toString().substr(2,10)+'.'+fileType;
varreadStream=fs.createReadStream(file.path);
varwriteStream=fs.createWriteStream(uploadPath+newFileName);
vardeferred=Q.defer();
readStream.pipe(writeStream);
readStream.on('end',deferred.makeNodeResolver());
returndeferred.promise.then(function(){
fs.unlinkSync(file.path);
return{
fileName:newFileName,
filePath:'/'+pathName+newFileName,
fileSize:file.size/1024>1024?(~~(10*file.size/1024/1024))/10+"MB":~~(file.size/1024)+"KB"
};
});
});
};
修改之后的代码可以支持Android手机,包括微信浏览器。注意不是所有的Android手机都会存在该问题,如果你发现在Andriod手机上无法上传图片,尤其是在微信浏览器中,则可以尝试下上面的方法。
以上所述是小编给大家介绍的关于安卓手机微信浏览器中使用XMLHttpRequest2上传图片显示字节数为0的解决办法,希望对大家有所帮助!