关于安卓手机微信浏览器中使用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库与其他JS库冲突的解决办法
Feb 07 Javascript
在JQuery dialog里的服务器控件 事件失效问题
Dec 08 Javascript
让innerText在firefox火狐和IE浏览器都能用的写法
May 14 Javascript
JS字符串函数扩展代码
Sep 13 Javascript
JS定义回车事件(实现代码)
Jul 08 Javascript
JavaScript使用encodeURI()和decodeURI()获取字符串值的方法
Aug 04 Javascript
jquery easyui validatebox remote的使用详解
Nov 09 Javascript
使用node.js中的Buffer类处理二进制数据的方法
Nov 26 Javascript
jQuery仿移动端支付宝键盘的实现代码
Aug 15 jQuery
Vue中通过vue-router实现命名视图的问题
Apr 23 Javascript
vue操作dom元素的3种方法示例
Sep 20 Javascript
Openlayers+EasyUI Tree动态实现图层控制
Sep 28 Javascript
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代码中使用换行及(\n或\r\n和br)的应用
2013/02/02 PHP
输入值/表单提交参数过滤有效防止sql注入的方法
2013/12/25 PHP
php截取字符串之截取utf8或gbk编码的中英文字符串示例
2014/03/12 PHP
php版淘宝网查询商品接口代码示例
2014/06/17 PHP
PHP清除字符串中所有无用标签的方法
2014/12/01 PHP
php实现常见图片格式的水印和缩略图制作(面向对象)
2016/06/15 PHP
设定php简写功能的方法
2019/11/28 PHP
Jquery Ajax学习实例7 Ajax所有过程事件分析示例
2010/03/23 Javascript
一些常用的JavaScript函数(json)附详细说明
2011/05/25 Javascript
固定背景实现的背景滚动特效示例分享
2013/05/19 Javascript
js判读浏览器是否支持html5的canvas的代码
2013/11/18 Javascript
浅谈箭头函数写法在ReactJs中的使用
2017/08/22 Javascript
JS闭包原理与应用经典示例
2018/12/20 Javascript
jQuery添加新内容的四个常用方法分析【append,prepend,after,before】
2019/03/19 jQuery
[46:23]完美世界DOTA2联赛PWL S2 FTD vs Magma 第一场 11.20
2020/11/23 DOTA
python的描述符(descriptor)、装饰器(property)造成的一个无限递归问题分享
2014/07/09 Python
Python标准库之随机数 (math包、random包)介绍
2014/11/25 Python
Python生成随机验证码的两种方法
2015/12/22 Python
python3.0 模拟用户登录,三次错误锁定的实例
2017/11/02 Python
Django数据库连接丢失问题的解决方法
2018/12/29 Python
Python实现的多进程拷贝文件并显示百分比功能示例
2019/04/09 Python
Python提取转移文件夹内所有.jpg文件并查看每一帧的方法
2019/06/27 Python
python__name__原理及用法详解
2019/11/02 Python
python数据处理——对pandas进行数据变频或插值实例
2020/04/22 Python
完美解决python针对hdfs上传和下载的问题
2020/06/05 Python
python中return不返回值的问题解析
2020/07/22 Python
屈臣氏官方旗舰店:亚洲享负盛名的保健及美妆零售商
2019/03/15 全球购物
中国包裹转运寄送国际服务:Famiboat
2019/07/24 全球购物
英国电子专家:maplin
2019/09/04 全球购物
下列程序在32位linux或unix中的结果是什么
2015/01/26 面试题
大学在校生求职信范文
2013/11/21 职场文书
助人为乐模范事迹材料
2014/06/02 职场文书
公司优秀员工推荐信
2015/03/24 职场文书
2015年小学生国庆节演讲稿
2015/07/30 职场文书
创业计划书之孕婴生活馆
2019/11/11 职场文书
Android Gradle 插件自定义Plugin实现注意事项
2022/06/16 Java/Android