In front-end JS, you use XMLHttpRequest 2 to upload images to the server. It is normal on PC and most mobile phones, but uploading on a small number of Android phones fails. When viewing the image on the server, the number of bytes is displayed is 0. Here is the core code for uploading the image:
HTML
<input type="file" id="choose" capture="camera" accept="image/*"> JavaScriptvar 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("Only 1 picture can be uploaded at a time");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 prime = 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);}};//Use progress event to display data sending progress xhr.upload.addEventListener('progress', function (e) {pecent = ~~(100 * e.loaded / e.total) / 2;//Use pecent to display upload progress}, 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});}}The above code uses FormData to implement form data submission. FormData is a new data type designed for XHR2. Using it, we can easily create HTML <Form> in JavaScript in real time and submit the form via AJAX. In the above code, the field in the submitted form is named imagefile and the value is blob, which is a file blob constructed and returned by the getBlob function. Uploading files through this method is simple and intuitive.
Then we receive and save the picture on the server and return the information of the uploaded picture.
Here is an example of Node.js code:
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;We use the formidable package to receive the uploaded file data, and then save the file to the /public/uploadImgs/dealInfo directory (assuming that public has been set to the root directory of static in Express), and rename the image according to the specified rules to ensure that the uploaded image will not be overwritten because of the same name. In addition, Q is used in the code to avoid using callback functions directly to better separate function functions.
The above code works normally on PC browsers and most mainstream mobile devices, but a small number of Android devices will have the uploaded image bytes of 0. For specific reasons, you can see the descriptions on the following web pages:
That means this is a bug in Android!
So how to solve it?
In fact, the answer can be found from the page given above, that is, we have to change the file upload method. In XHR2, in addition to uploading files in Blob, you can also upload files in ArrayBuffer.
The following is the modified front-end JavaScript code:
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("Only 1 picture can be uploaded at a time");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 prime = 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);}};//Use progress event to display data sending progress xhr.upload.addEventListener('progress', function (e) {pecent = ~~(100 * e.loaded / e.total) / 2;//Use pecent to display upload progress}, false);xhr.send(buffer.buffer); // Upload image in ArrayBuffer}I'll highlight the changes. Uploading images in ArrayBuffer mode must add the RequestHeader of 'application/octet-stream', otherwise the server will not be able to respond to the request. In addition, by uploading pictures in this way, we cannot obtain the file type from the form data. We can pass the file type to the server in a query manner, and the server generates the corresponding file according to the file type. The following is the server code after a small amount of modification:
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"};});});};The modified code can support Android phones, including WeChat browsers. Note that not all Android phones will have this problem. If you find that you cannot upload images on Android phones, especially in WeChat browsers, you can try the above method.
The above is the solution to uploading images to 0 using XMLHttpRequest 2 in the WeChat browser of Android mobile phones. I hope it will be helpful to everyone!