如何在移动web上上传文件..

xmlhttprequest2.0 可以支持文件上传.这东东很方便,但是在实际使用中碰到了一些问题.这里记录下.

正常情况下我们是这样生成2进制文件的.

//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);

愉快的上传….然后…然后…没有然后了…抓包看下.

------WebKitFormBoundarysToAVAYMLPFfJF96
 Content-Disposition: form-data; name="img"; filename="blob"
 Content-Type: application/octet-stream ------WebKitFormBoundarysToAVAYMLPFfJF96--

文件的内容呢…

好吧..既然没法愉快用formdata..那么就自己动手生成一个post的包体吧…下面是相关的代码..

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);
        }
    };
}

最后完整的代码长这样

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;
        };

转载:

http://stackoverflow.com/questions/15639070/empty-files-uploaded-in-android-native-browser/28809955#28809955

http://hao.jser.com/archive/7247/

http://www.haomou.net/2016/01/14/2016_android_blob/

http://www.oschina.net/question/2502182_2139420

转载于:https://my.oschina.net/u/1260221/blog/657797

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值