关于安卓手机微信浏览器中使用XMLHttpRequest 2上传图片显示字节数为0的解决办法


Posted in Javascript onMay 17, 2016

 前端JS中使用XMLHttpRequest 2上传图片到服务器,PC端和大部分手机上都正常,但在少部分安卓手机上上传失败,服务器上查看图片,显示字节数为0。下面是上传图片的核心代码:

HTML

<input type="file" id="choose" capture="camera" accept="image/*"> 
JavaScript
var filechooser = document.getElementById("choose");
filechooser.onchange = function () {
var _this = $(this);
if (!this.files.length) return;
var files = 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;
var reader = new FileReader();
reader.onload = function () {
var result = this.result;
upload(result, file.type);
};
reader.readAsDataURL(file);
});
};
function upload(basestr, type){
var xhr = new XMLHttpRequest();
var text = window.atob(basestr.split(",")[1]);
var buffer = new Uint8Array(text.length);
var pecent = 0;
for (var i = 0; i < text.length; i++) {
buffer[i] = text.charCodeAt(i);
}
var blob = getBlob(buffer, type);
var formdata = new FormData();
formdata.append('imagefile', blob);
xhr.open('post', '/uploadtest');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var jsonData = 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);
}
function getBlob(buffer, format){
var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
if(Builder){
var builder = new Builder();
builder.append(buffer);
return builder.getBlob(format);
} else {
return new window.Blob([ buffer ], {type: format});
}
}

上述代码使用FormData来实现表单数据提交。FormData是一种针对XHR2设计的新型数据类型,使用它我们可以很方便地实时以JavaScript创建HTML <Form>,然后通过AJAX提交该表单。在上述代码中,提交的表单中的字段名为imagefile,值是blob,这是一个通过getBlob函数构造并返回的文件Blob。通过该方法上传文件简单直观。

然后我们在服务端接收并保存图片,并返回已上传的图片的信息。

下面是Node.js代码的示例:

var Q = require('q');
var fs = require('fs');
var path = require('path');
var formidable = require('formidable');
var moment = require('moment');var imageUpload = function (){ };
imageUpload.prototype.useFormParseCallback = function(req){
var deferred = Q.defer();
var form = new formidable.IncomingForm();
form.parse(req, deferred.makeNodeResolver());
return deferred.promise;
};
imageUpload.prototype.uploadImageTest = function(req){
var pathName = 'uploadImgs/dealInfo/';
var uploadPath = path.join(__dirname, '../../public/', pathName);
return this.useFormParseCallback(req).then(function(files){
var file = files[1].imagefile;
var fileType = files[1].imagefile.type.split('/')[1];
var newFileName = 'upload_' + moment().format('x') + Math.random().toString().substr(2, 10) + '.' + fileType;
var readStream = fs.createReadStream(file.path);
var writeStream = fs.createWriteStream(uploadPath + newFileName);
var deferred = Q.defer();
readStream.pipe(writeStream);
readStream.on('end', deferred.makeNodeResolver());
return deferred.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代码:

var filechooser = document.getElementById("choose");
filechooser.onchange = function () {
var _this = $(this);
if (!this.files.length) return;
var files = 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;
var reader = new FileReader();
reader.onload = function () {
var result = this.result;
upload(result, file.type);
};
reader.readAsDataURL(file);
});
};
function upload(basestr, type){
var xhr = new XMLHttpRequest();
var text = window.atob(basestr.split(",")[1]);
var buffer = new Uint8Array(text.length);
var pecent = 0;
for (var i = 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) {
var jsonData = 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){
var pathName = 'uploadImgs/dealInfo/';
var uploadPath = path.join(__dirname, '../../public/', pathName);
return this.useFormParseCallback(req).then(function(files){
var file = files[1].file;
var fileType = req.query.filetype ? ('.' + req.query.filetype) : '.png';
var newFileName = 'upload_' + moment().format('x') + Math.random().toString().substr(2, 10) + '.' + fileType;
var readStream = fs.createReadStream(file.path);
var writeStream = fs.createWriteStream(uploadPath + newFileName);
var deferred = Q.defer();
readStream.pipe(writeStream);
readStream.on('end', deferred.makeNodeResolver());
return deferred.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手机上无法上传图片,尤其是在微信浏览器中,则可以尝试下上面的方法。

以上所述是小编给大家介绍的关于安卓手机微信浏览器中使用XMLHttpRequest 2上传图片显示字节数为0的解决办法,希望对大家有所帮助!

Javascript 相关文章推荐
JQuery 前台切换网站的样式实现
Jun 22 Javascript
JavaScript 对象链式操作测试代码
Apr 25 Javascript
100个不能错过的实用JS自定义函数
Mar 05 Javascript
微信小程序 教程之事件
Oct 18 Javascript
浅谈JavaScript的自动垃圾收集机制
Dec 15 Javascript
详解基于angular路由的requireJs按需加载js
Jan 20 Javascript
微信小程序开发之从相册获取图片 使用相机拍照 本地图片上传
Apr 18 Javascript
详解vue-cli构建项目反向代理配置
Sep 07 Javascript
使用 Vue 绑定单个或多个 Class 名的实例代码
Jan 08 Javascript
JS Object.preventExtensions(),Object.seal()与Object.freeze()用法实例分析
Aug 25 Javascript
jQuery插件实现非常实用的tab栏切换功能【案例】
Feb 18 jQuery
vue使用refs获取嵌套组件中的值过程
Mar 31 Vue.js
Web前端新人笔记之jquery入门心得(新手必看)
May 17 #Javascript
Angularjs中的事件广播 —全面解析$broadcast,$emit,$on
May 17 #Javascript
iScroll.js 使用方法参考
May 16 #Javascript
BootStrap的JS插件之轮播效果案例详解
May 16 #Javascript
老司机带你解读jQuery插件开发流程
May 16 #Javascript
jQuery 获取多选框的值及多选框中文的函数
May 16 #Javascript
jQuery插件制作的实例教程
May 16 #Javascript
You might like
PHP下打开URL地址的几种方法小结
2010/05/16 PHP
php中switch与ifelse的效率区别及适用情况分析
2015/02/12 PHP
浅谈php自定义错误日志
2015/02/13 PHP
php类自动装载、链式操作、魔术方法实现代码
2017/07/23 PHP
Firebug 字幕文件JSON地址获取代码
2009/10/28 Javascript
jQuery实现的立体文字渐变效果
2010/05/17 Javascript
jquery.fileEveryWhere.js 一个跨浏览器的file显示插件
2011/10/24 Javascript
php与js的区别是什么
2013/08/05 Javascript
javascript事件模型实例分析
2015/01/30 Javascript
浅谈jquery.fn.extend与jquery.extend区别
2015/07/13 Javascript
开启Javascript中apply、call、bind的用法之旅模式
2015/10/28 Javascript
深入解析JavaScript中函数的Currying柯里化
2016/03/19 Javascript
详述JavaScript实现继承的几种方式(推荐)
2016/03/22 Javascript
DataTables+BootStrap组合使用Ajax来获取数据并且动态加载dom的方法(排序,过滤,分页等)
2016/11/09 Javascript
利用PM2部署node.js项目的方法教程
2017/05/10 Javascript
JavaScript实现精美个性导航栏筋斗云效果
2017/10/29 Javascript
vue-router配合ElementUI实现导航的实例
2018/02/11 Javascript
微信小程序仿RadioGroup改变样式的处理方案
2018/07/13 Javascript
详解Vue底部导航栏组件
2019/05/02 Javascript
举例详解Python中threading模块的几个常用方法
2015/06/18 Python
Python进程间通信之共享内存详解
2017/10/30 Python
python导出hive数据表的schema实例代码
2018/01/22 Python
Python中__slots__属性介绍与基本使用方法
2018/09/05 Python
Python Selenium 之关闭窗口close与quit的方法
2019/02/13 Python
python进行参数传递的方法
2020/05/12 Python
Python项目实战之使用Django框架实现支付宝付款功能
2021/02/23 Python
北美女性服装零售连锁店:maurices
2019/06/12 全球购物
MYSQL基础面试题
2012/05/13 面试题
客户代表自我评价范例
2013/09/24 职场文书
开学典礼感言
2014/02/16 职场文书
入党积极分子对十八届四中全会期盼的思想汇报
2014/10/17 职场文书
2014年学校后勤工作总结
2014/12/06 职场文书
初中政治教学反思
2016/02/23 职场文书
大学生各类奖学金申请书
2019/06/24 职场文书
python numpy中multiply与*及matul 的区别说明
2021/05/26 Python
Python 数据可视化工具 Pyecharts 安装及应用
2022/04/20 Python