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

 更新时间:2016年05月17日 09:09:14   作者:Jaxu  
这篇文章主要介绍了关于安卓手机微信浏览器中使用XMLHttpRequest 2上传图片显示字节数为0的解决办法 的相关资料,需要的朋友可以参考下

 前端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的解决办法,希望对大家有所帮助!

相关文章

  • 浅谈微信小程序flex布局基础

    浅谈微信小程序flex布局基础

    这篇文章主要介绍了浅谈微信小程序flex布局基础,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • IE下使用cloneNode注意事项分享

    IE下使用cloneNode注意事项分享

    在开发百度“地裂特效”时,发现了一些匪夷所思的 bug,第一反应是特效本身的代码与页面上原有的脚本发生了冲突,经过调试发现,问题出现在 cloneNode 的应用上
    2012-11-11
  • js阻止事件追加的具体实现

    js阻止事件追加的具体实现

    可以使用 e.stopPropagation(); e.preventDefault();来阻止事件冒泡,和默认事件的执行。但不能阻止事件的追加,如要追加,请看下面的实现方法
    2014-10-10
  • JavaScript实现省市联动过程中bug的解决方法

    JavaScript实现省市联动过程中bug的解决方法

    这篇文章主要为大家详细介绍了解决JavaScript实现省市联动过程中的bug,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • 阻止mousemove鼠标移动或touchmove触摸移动触发click点击事件

    阻止mousemove鼠标移动或touchmove触摸移动触发click点击事件

    这篇文章主要为大家介绍了阻止mousemove或touchmove与click事件同时触发技巧,一个按钮绑定了多个事件,所以就要想办法阻止 mouse 鼠标事件或 touch 触摸事件 与 click 事件同时触发,不然每次拖拽按钮后都会触发 click 事件,这显然是不友好的
    2023-06-06
  • ES6新增数据结构WeakSet的用法详解

    ES6新增数据结构WeakSet的用法详解

    WeakSet和Set类似,同样是元素不重复的集合,它们的区别是WeakSet内的元素必须是对象,不能是其它类型。接下来通过本文给大家详细介绍ES6新增数据结构WeakSet的用法,感兴趣的朋友一起看看吧
    2017-08-08
  • 帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

    帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

    这篇文章主要介绍了详解帮你彻底搞懂JS中的prototype、__proto__与constructor(图解),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • 基于javascript实现图片预加载

    基于javascript实现图片预加载

    这篇文章主要介绍了javascript图片预加载的方法,实例分析了javascript实现图片预加载的思路,具有一定参考借鉴价值,需要的朋友可以参考下
    2016-01-01
  • JS中Symbol类型的介绍与基本用法

    JS中Symbol类型的介绍与基本用法

    Symbol是一种特殊的、不可变的数据类型,可以作为对象属性的标识符使用,表示独一无二的值,这篇文章主要给大家介绍了关于JS中Symbol类型的介绍与基本用法的相关资料,需要的朋友可以参考下
    2024-04-04
  • 使用JavaScript实现在页面中显示距离2017年中秋节的天数

    使用JavaScript实现在页面中显示距离2017年中秋节的天数

    这篇文章主要介绍了 使用JavaScript实现在页面中显示距离2017年中秋节的天数的相关资料,需要的朋友可以参考下
    2017-09-09

最新评论