xmlhttprequest2.0 能夠支持文件上傳.這東東很方便,可是在實際使用中碰到了一些問題.這裏記錄下.android
正常狀況下咱們是這樣生成2進制文件的.canvas
//data爲文件的base64編碼 function dataURLtoBlob(data) { var tmp = data.split(','); tmp[1] = tmp[1].replace(//s/g,''); var binary = atob(tmp[1]); var array = []; for(var i = 0; i < binary.length; i++) { array.push(binary.charCodeAt(i)); } return new Blob([new Uint8Array(array)], {type: 'image/jpeg'}); }
可是在android手機上可能會因爲沒有blob對象致使沒法生成blob.怎麼辦捏.可使用如下代碼:瀏覽器
function newBlob(data, datatype){ var out; try { out = new Blob([data], {type: datatype}); //一切正常,直接使用blob. } catch (e) { window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; if (e.name == 'TypeError' && window.BlobBuilder) { var bb = new BlobBuilder(); bb.append(data.buffer); out = bb.getBlob(datatype); //還能夠搶救一下..使用blobbuilder來生成文件.. } else { //沒救了,放棄治療. } } return out; }
ok.如今文件已經ready了.服務器
咱們建立一個formadata微信
var file = dataURLtoBlob(img); fd.append('img',file);
愉快的上傳….而後…而後…沒有而後了…抓包看下.app
------WebKitFormBoundarysToAVAYMLPFfJF96 Content-Disposition: form-data; name="img"; filename="blob" Content-Type: application/octet-stream ------WebKitFormBoundarysToAVAYMLPFfJF96--
文件的內容呢…dom
好吧..既然無法愉快用formdata..那麼就本身動手生成一個post的包體吧…下面是相關的代碼..ide
function FormDataShim () { var o = this, parts = [],// Data to be sent boundary = Array(5).join('-') + (+new Date() * (1e16*Math.random())).toString(32), oldSend = XMLHttpRequest.prototype.send; this.append = function (name, value, filename) { parts.push('--' + boundary + '/r/nContent-Disposition: form-data; name="' + name + '"'); if (value instanceof Blob) { parts.push('; filename="'+ (filename || 'blob') +'"/r/nContent-Type: ' + value.type + '/r/n/r/n'); parts.push(value); } else { parts.push('/r/n/r/n' + value); } parts.push('/r/n'); }; //把xhr的send方法重寫一下. XMLHttpRequest.prototype.send = function (val) { var fr, data, oXHR = this; if (val === o) { // 最後加一下boundary..注意這裏必定要在最後加/r/n..不然服務器有可能會解析參數失敗.. parts.push('--' + boundary + '--/r/n'); data = new XBlob(parts); fr = new FileReader(); fr.onload = function () { oldSend.call(oXHR, fr.result); }; fr.onerror = function (err) { throw err; }; fr.readAsArrayBuffer(data); // 設置content-type this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary); XMLHttpRequest.prototype.send = oldSend; } else { oldSend.call(this, val); } }; }
最後完整的代碼長這樣post
function newBlob(data, datatype){ var out; try { out = new Blob([data], {type: datatype}); } catch (e) { window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; if (e.name == 'TypeError' && window.BlobBuilder) { var bb = new BlobBuilder(); bb.append(data.buffer); out = bb.getBlob(datatype); } else if (e.name == "InvalidStateError") { out = new Blob([data], {type: datatype}); } else { } } return out; } // 判斷是否須要blobbuilder var needsFormDataShim = (function () { var bCheck = ~navigator.userAgent.indexOf('Android') && ~navigator.vendor.indexOf('Google') && !~navigator.userAgent.indexOf('Chrome'); return bCheck && navigator.userAgent.match(/AppleWebKit\/(\d+)/).pop() <= 534; })(), blobConstruct = !!(function () { try { return new Blob(); } catch (e) {} })(), XBlob = blobConstruct ? window.Blob : function (parts, opts) { var bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder); parts.forEach(function (p) { bb.append(p); }); return bb.getBlob(opts ? opts.type : undefined); }; function FormDataShim () { // Store a reference to this var o = this, parts = [],// Data to be sent boundary = Array(5).join('-') + (+new Date() * (1e16*Math.random())).toString(32), oldSend = XMLHttpRequest.prototype.send; this.append = function (name, value, filename) { parts.push('--' + boundary + '\r\nContent-Disposition: form-data; name="' + name + '"'); if (value instanceof Blob) { parts.push('; filename="'+ (filename || 'blob') +'"\r\nContent-Type: ' + value.type + '\r\n\r\n'); parts.push(value); } else { parts.push('\r\n\r\n' + value); } parts.push('\r\n'); }; // Override XHR send() XMLHttpRequest.prototype.send = function (val) { var fr, data, oXHR = this; if (val === o) { //注意不能漏最後的\r\n ,不然有可能服務器解析不到參數. parts.push('--' + boundary + '--\r\n'); data = new XBlob(parts); fr = new FileReader(); fr.onload = function () { oldSend.call(oXHR, fr.result); }; fr.onerror = function (err) { throw err; }; fr.readAsArrayBuffer(data); this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary); XMLHttpRequest.prototype.send = oldSend; } else { oldSend.call(this, val); } }; } //把圖片轉成formdata 可使用的數據... //這裏要把\s替換掉..要否則atob的時候會出錯.... function dataURLtoBlob(data) { var tmp = data.split(','); tmp[1] = tmp[1].replace(/\s/g,''); var binary = atob(tmp[1]); var array = []; for(var i = 0; i < binary.length; i++) { array.push(binary.charCodeAt(i)); } return new newBlob(new Uint8Array(array), 'image/jpeg'); } function uploadFile(img){ var fd = needsFormDataShim ? new FormDataShim() : new FormData(); var file = dataURLtoBlob(img); fd.append('img',file); var prog = function(e){ /*你的邏輯*/ } var load = function(e){ /*你的邏輯*/ } var error = function(e){ /*你的邏輯*/ } var abort = function(e){ /*你的邏輯*/ } var xhr = new XMLHttpRequest(); xhr.upload.addEventListener('progress',prog,false); xhr.addEventListener('load',load,false); xhr.addEventListener('error',error,false); xhr.addEventListener('abort',abort,false); xhr.onreadystatechange = function(){ /*你的邏輯*/ } xhr.open('POST','/upload',true); xhr.send(fd); }
document.addEventListener('DOMContentLoaded', init, false); function init() { var u = new UploadPic(); u.init({ input: document.querySelector('#selectFile') }); } function UploadPic() { this.sw = 0; this.sh = 0; this.tw = 0; this.th = 0; this.scale = 0; this.maxSize = 0; this.fileSize = 0; this.fileDate = null; this.fileType = ''; this.fileName = ''; this.input = null; this.canvas = null; this.mime = {}; this.type = ''; this.callback = function () { }; this.loading = function () { }; } UploadPic.prototype.init = function (options) { this.input = options.input; this.mime = {'png': 'image/png', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'bmp': 'image/bmp'}; this.callback = options.callback || function () { }; this._addEvent(); }; UploadPic.prototype._addEvent = function () { var _this = this; function tmpSelectFile(ev) { _this._handelSelectFile(ev); } this.input.addEventListener('change', tmpSelectFile, false); }; UploadPic.prototype._handelSelectFile = function (ev) { var file = ev.target.files[0]; this.type = file.type; // 若是沒有文件類型,則經過後綴名判斷(解決微信及360瀏覽器沒法獲取圖片類型問題) if (!this.type) { this.type = this.mime[file.name.match(/\.([^\.]+)$/i)[1]]; } if (!/image.(png|jpg|jpeg|bmp)/.test(this.type)) { alert('選擇的文件類型不是圖片'); return; } if (file.size > this.maxSize) { alert('選擇文件大於' + this.maxSize / 1024 / 1024 + 'M,請從新選擇'); return; } this.fileName = file.name; this.fileSize = file.size; this.fileType = this.type; this.fileDate = file.lastModifiedDate; this._readImage(file); }; UploadPic.prototype._readImage = function (file) { var _this = this; this._getURI(file, this.callback); }; UploadPic.prototype._getURI = function (file, callback) { var reader = new FileReader(); var _this = this; function tmpLoad() { // 頭不帶圖片格式,需填寫格式 var re = /^data:base64,/; var ret = this.result + ''; if (re.test(ret)) ret = ret.replace(re, 'data:' + _this.mime[_this.fileType] + ';base64,'); callback && callback(ret); } reader.onload = tmpLoad; reader.readAsDataURL(file); return false; };
轉載:
ui
http://hao.jser.com/archive/7247/