首页 > 代码库 > 解决微信内嵌浏览器无法响应上传文件(图片)的思路(2种办法)

解决微信内嵌浏览器无法响应上传文件(图片)的思路(2种办法)

      进园一年多来,第一次写博客,好激动。原因主要是自己平时都是有写笔记,不习惯写博客,这次想写博客的原因是,这个问题确实我做了很久,已经做了近两周才解决了这个问题,,而且两周时间里尝试过了很多种办法,然后由于网上又没有多少人分享这个,决定自己写一下。

 

      先自我介绍下,我是惠州学院大二(准大三)的学生,在去年暑假时候加了一个工作室开始写代码,也是在那个时候加入博客园,最近因为工作室要做微信端网页的开发需要一个上传图片功能,然后编码的时候发现用平时的方法做的上传功能在Android的微信端上实现不了,百度一下发现很多人也出现过同样的问题,也有人指出说这是被腾讯给“阉割”掉了的,经过两个星期的尝试后,找到了两种解决办法。

  • 我尝试过的办法

    •        首先是<input type=”file”>,这是我们最容易,同时也是最先用的,应该也是我试过那么多的插件都是用这个,这个只有在部分手机的微信版本上会显示upload disable,大部分手机微信还是支持的,但是做完我们会发现我们的微信可以选择文件,但是上传不了,百度了一下很多人说这个被腾讯给“阉割”掉了,一开始我也是这么觉得,所以就放弃了选择这个,后来发现,腾讯“阉割”掉了的不是<input type=”file”>,而是你上传往服务器发请求的那一块,具体怎么解释我是新手,真心不造怎么说,后面会再说,希望知道这个原理的能和我说明一下。
    •        放弃了<input type=”file”>,我们工作室的师兄就推荐我一款插件,叫Uploadify,这款插件在它的官网上面有两种版本,一种是flash(免费),一种是HTML5(收费),不过也有大牛把HTML5的版本模仿了出来,网上找得到。首先是flash吧,这个也是另外一篇博文里博主推荐大家的方法(原文地址:http://blog.csdn.net/zz880329/article/details/12652063),我就尝试了一下,发现使用flash实现上传的话,选择文件的“样式”变了,变成了flash的选择文件,大家自己试过的话应该就会发现,flash选择文件的方式你看到的只有文件名,你都不知道自己要选哪张图片好,而且好像还需要手机有flash支持,所以这个flash被我放弃了。然后是HTML5的版本,打开demo的代码,上面确实没有了<input type=”file”>,但是实际运行起来,在网页还是用的是了<input type=”file”>,实际放到微信上好像也是行不通,后来也就放弃了。
    •        后面又看到一篇介绍各种HTML5的上传插件的博文,然后又是像无头苍蝇那样各种尝试,最终只保留下了其中一种我觉得是最可行的,但是好像兼容性各方面还有待优化,后面我的第二种可行办法我会再介绍它的,希望大家都说下各自的理解吧,还有好多方面我都不太懂。
  • 第一种可行的js插件

    •        尝试过很多插件之后,基本都以失败告终。不过好像每次写代码都很幸运,我舍友在微信上找到一个网站是有实现相关功能的,然后我就去找到那个网站研究他们的上传插件,发现使用的插件我之前没有尝试过,于是就找到了这个插件并且成功实现功能,插件叫ajaxfileupload.js
      //这段代码用于解决一些JQuery版本缺少的一个函数jQuery.extend({    handleError: function (s, xhr, status, e) {        // If a local callback was specified, fire it        if (s.error) {            s.error.call(s.context || s, xhr, status, e);        }        // Fire the global callback        if (s.global) {            (s.context ? jQuery(s.context) : jQuery.event).trigger("ajaxError", [xhr, s, e]);        }    },//ajaxfileupload.js源代码    createUploadIframe: function(id, uri)    {            //create frame            var frameId = ‘jUploadFrame‘ + id;            var iframeHtml = ‘<iframe id="‘ + frameId + ‘" name="‘ + frameId + ‘" style="position:absolute; top:-9999px; left:-9999px"‘;            if(window.ActiveXObject)            {                if(typeof uri== ‘boolean‘){                    iframeHtml += ‘ src="‘ + ‘javascript:false‘ + ‘"‘;                }                else if(typeof uri== ‘string‘){                    iframeHtml += ‘ src="http://www.mamicode.com/‘ + uri + ‘"‘;                }                }            iframeHtml += ‘ />‘;            jQuery(iframeHtml).appendTo(document.body);            return jQuery(‘#‘ + frameId).get(0);                },    createUploadForm: function(id, fileElementId, data)    {        //create form            var formId = ‘jUploadForm‘ + id;        var fileId = ‘jUploadFile‘ + id;        var form = jQuery(‘<form  action="" method="POST" name="‘ + formId + ‘" id="‘ + formId + ‘" enctype="multipart/form-data"></form>‘);            if(data)        {            for(var i in data)            {                jQuery(‘<input type="hidden" name="‘ + i + ‘" value="http://www.mamicode.com/‘ + data[i] + ‘" />‘).appendTo(form);            }                    }                var oldElement = jQuery(‘#‘ + fileElementId);        var newElement = jQuery(oldElement).clone();        jQuery(oldElement).attr(‘id‘, fileId);        jQuery(oldElement).before(newElement);        jQuery(oldElement).appendTo(form);                //set attributes        jQuery(form).css(‘position‘, ‘absolute‘);        jQuery(form).css(‘top‘, ‘-1200px‘);        jQuery(form).css(‘left‘, ‘-1200px‘);        jQuery(form).appendTo(‘body‘);                return form;    },    ajaxFileUpload: function(s) {        // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout                s = jQuery.extend({}, jQuery.ajaxSettings, s);        var id = new Date().getTime()                var form = jQuery.createUploadForm(id, s.fileElementId, (typeof(s.data)==‘undefined‘?false:s.data));        var io = jQuery.createUploadIframe(id, s.secureuri);        var frameId = ‘jUploadFrame‘ + id;        var formId = ‘jUploadForm‘ + id;                // Watch for a new set of requests        if ( s.global && ! jQuery.active++ )        {            jQuery.event.trigger( "ajaxStart" );        }                    var requestDone = false;        // Create the request object        var xml = {}           if ( s.global )            jQuery.event.trigger("ajaxSend", [xml, s]);        // Wait for a response to come back        var uploadCallback = function(isTimeout)        {                        var io = document.getElementById(frameId);            try             {                                if(io.contentWindow)                {                     xml.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;                     xml.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;                                     }else if(io.contentDocument)                {                     xml.responseText = io.contentDocument.document.body?io.contentDocument.document.body.innerHTML:null;                    xml.responseXML = io.contentDocument.document.XMLDocument?io.contentDocument.document.XMLDocument:io.contentDocument.document;                }                                    }catch(e)            {                jQuery.handleError(s, xml, null, e);            }            if ( xml || isTimeout == "timeout")             {                                requestDone = true;                var status;                try {                    status = isTimeout != "timeout" ? "success" : "error";                    // Make sure that the request was successful or notmodified                    if ( status != "error" )                    {                        // process the data (runs the xml through httpData regardless of callback)                        var data =http://www.mamicode.com/ jQuery.uploadHttpData( xml, s.dataType );                            // If a local callback was specified, fire it and pass it the data                        if ( s.success )                            s.success( data, status );                            // Fire the global callback                        if( s.global )                            jQuery.event.trigger( "ajaxSuccess", [xml, s] );                    } else                        jQuery.handleError(s, xml, status);                } catch(e)                 {                    status = "error";                    jQuery.handleError(s, xml, status, e);                }                // The request was completed                if( s.global )                    jQuery.event.trigger( "ajaxComplete", [xml, s] );                // Handle the global AJAX counter                if ( s.global && ! --jQuery.active )                    jQuery.event.trigger( "ajaxStop" );                // Process result                if ( s.complete )                    s.complete(xml, status);                jQuery(io).unbind()                setTimeout(function()                                    {    try                                         {                                            jQuery(io).remove();                                            jQuery(form).remove();                                                                                        } catch(e)                                         {                                            jQuery.handleError(s, xml, null, e);                                        }                                                                        }, 100)                xml = null            }        }        // Timeout checker        if ( s.timeout > 0 )         {            setTimeout(function(){                // Check to see if the request is still happening                if( !requestDone ) uploadCallback( "timeout" );            }, s.timeout);        }        try         {            var form = jQuery(‘#‘ + formId);            jQuery(form).attr(‘action‘, s.url);            jQuery(form).attr(‘method‘, ‘POST‘);            jQuery(form).attr(‘target‘, frameId);            if(form.encoding)            {                jQuery(form).attr(‘encoding‘, ‘multipart/form-data‘);                              }            else            {                    jQuery(form).attr(‘enctype‘, ‘multipart/form-data‘);                        }                        jQuery(form).submit();        } catch(e)         {                        jQuery.handleError(s, xml, null, e);        }                jQuery(‘#‘ + frameId).load(uploadCallback    );        return {abort: function () {}};        },    uploadHttpData: function( r, type ) {        var data = http://www.mamicode.com/!type;        data = type == "xml" || data ? r.responseXML : r.responseText;        // If the type is "script", eval it in global context        if ( type == "script" )            jQuery.globalEval( data );        // Get the JavaScript object, if JSON is used.        if ( type == "json" )            eval("data = "http://www.mamicode.com/+ data);        //eval("data = http://www.mamicode.com/" " + data + " \" ");//此处根据网络版本修改的,修改后发现没用用,用于解决json问题        // evaluate scripts within html        if ( type == "html" )            jQuery("<div>").html(data).evalScripts();        return data;    }})
      • 前台代码很简单
      <!DOCTYPE html><html><head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">    <title>Insert title here</title>    <meta charset="utf-8">    <meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1">    <script src="jquery-1.7.1.js" type="text/javascript"></script>    <script src="ajaxfileupload.js" type="text/javascript"></script>    <script type="text/javascript">        function ajaxFileUpload() {            $.ajaxFileUpload            (                {                    url: upload.aspx, //用于文件上传的服务器端请求地址                    secureuri: false, //一般设置为false                    fileElementId: file, //文件上传空间的id属性  <input type="file" id="file" name="file" />                    dataType: json, //返回值类型 一般设置为json                    success: function (data, status)  //服务器成功响应处理函数                    {                        $("#img1").attr("src", data.imgurl);                        if (typeof (data.error) != undefined) {                            if (data.error != ‘‘) {                                alert(data.error);                            } else {                                alert(data.msg);                            }                        }                    },                    error: function (data, status, e)//服务器响应失败处理函数                    {                        alert(e);                    }                }            )            return false;        }    </script></head><body>    <input type="file" id="file" name="file"  value="上传" accept="image/*;capture=camera"  onchange="ajaxFileUpload()"/>    <p><img id="img1" alt="上传成功啦" src="" /></p></body></html>
    • 然后是C#的后台代码,这里我就没有放压缩图片大小的代码进来了,压缩图片的功能是根据教程做的,也是可以实现后台压缩图片的
      using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;public partial class Upload_Upload : System.Web.UI.Page{    protected void Page_Load(object sender, EventArgs e)    {        HttpFileCollection files = Request.Files;//这里只能用<input type="file" />才能有效果,因为服务器控件是HttpInputFile类型        string filePath = "Upimages";        string msg = string.Empty;        string error = string.Empty;        string imgurl;        if (files.Count > 0)        {            files[0].SaveAs(Server.MapPath(filePath+"/") + System.IO.Path.GetFileName(files[0].FileName));            msg = " 成功! 文件大小为:" + files[0].ContentLength;            imgurl = "Upimages/" + files[0].FileName;            string res = "{ error:‘" + error + "‘, msg:‘" + msg + "‘,imgurl:‘" + imgurl + "‘}";            Response.Write(res);            Response.End();        }    }}
            接下来是我对这个插件以及这个插件为什么能实现的一点看法:我主要是在我调试的过程中发现了问题,因为之前也是调试其他不同的插件,不过我的解释可能说不太清楚,还是希望有人能帮我解答~,原因应该是在ajaxfileupload.js这个文件里边。在用firebug调试上传功能的时候,可以很明显看到,控制台内并没用任何内容;而当我使用其他插件的时候,firebug里边都会显示我先一般处理程序发送的请求,而且能够看到返回的信息。我觉得这就是为何这个插件能够实现上传的原因。具体的原理我不知道怎么去解释这个原理,还请大牛帮忙,谢谢!

          image

通过这个实现了之后,腾讯“阉割”掉的应该不是<input type="file”>而是你上传的文件的文件流,<input type="file”>并不会失效(除了在部分低端手机和HTC)。而这个插件把这关键的发送文件流的部分“隐藏了起来”,具体隐藏的方法应该就是他js文件写的,所以这个插件成功了。

    • 第二种可行的办法,仅限用于图片,其他类型文件我还没尝试

      • 接下来是第二种可行的办法,也是在找控件的过程中发现的,感觉很棒,我在三星的微信上试过可以实现上传,但是在魅族上页面还是出错了,之后由于第一种方法可以兼容,于是就采用了第一种,没有对第二种进行深究了,不过还是分享一下。它的原理就是把你点击选择完图片后,用js将你选择的图片转为base64格式的数据,而且还能实现使用js对图片进行压缩后保存下压缩后的base64格式的字符串数据,而我们只需要把这串字符串发送到后台,然后在后台将base64格式的数据转为图片就容易啦,这样,腾讯也抓不了。

      • 前台代码,我跟原作者代码有修改了一点,因为发现发送到后台base64转图片的时候,base64编码不能带上“文件头”(/^data:base64,/),还有就是加上了压缩图片所需要修改的参数的位置,这里默认压缩为100*100。(原作者好腻害的赶脚)。

      • <!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"><title>图片压缩</title>    <script src="jquery-1.7.1.js" type="text/javascript"></script><style>body { margin:0; padding:0; }html { font-size:62.5%; }.imgzip { padding:1em; }.imgzip .itm { padding-bottom:1em; word-break:break-all; font-size:1.2rem; line-height:1.5em; }.imgzip .itm .tit { margin-bottom:.5em; background-color:#e71446; color:#FFF; padding:.5rem 1rem; border-radius:3px; }.imgzip .itm .cnt { padding:1rem; }.imgzip .itm .cnt img { display:block; max-width:100%; }.imgzip textarea { width:100%; height:20em; }</style></head><body><input type="file" accept="image/*;capture=camera" class="input"><input type="button" value="上传" onclick="upload();" /><div class="imgzip"></div><script>    document.addEventListener(DOMContentLoaded, init, false);    function init() {        var u = new UploadPic();        u.init({            input: document.querySelector(.input),            callback: function (base64) {                var html = ‘‘;                html = <div class="itm"><div class="tit">图片名称:</div><div class="cnt" id="name"> + this.fileName + </div></div>           + <div class="itm"><div class="tit">原始大小:</div><div class="cnt"> + (this.fileSize / 1024).toFixed(2) + KB<\/div><\/div>           + <div class="itm"><div class="tit">编码大小:</div><div class="cnt"> + (base64.length / 1024).toFixed(2) + KB<\/div><\/div>           + <div class="itm"><div class="tit">原始尺寸:</div><div class="cnt"> + this.tw + px *  + this.th + px<\/div><\/div>           + <div class="itm"><div class="tit">编码尺寸:</div><div class="cnt"> + this.sw + px *  + this.sh + px<\/div><\/div>           + <div class="itm"><div class="tit">图片预览:</div><div class="cnt"><img src="http://www.mamicode.com/ + base64 + "><\/div><\/div>           + <div class="itm"><div class="tit">Base64编码:</div><div class="cnt"><textarea id="base64"> + this.noHead + <\/textarea><\/div><\/div>;                document.querySelector(.imgzip).innerHTML = html;            },            loading: function () {                document.querySelector(.imgzip).innerHTML = 读取中,请稍候...;            }        });    }    function UploadPic() {        this.sw = 0;        this.sh = 0;        this.tw = 0;        this.th = 0;        this.scale = 0;        this.maxWidth = 0;        this.maxHeight = 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 () { };        this.noHead = "";    }    UploadPic.prototype.init = function (options) {        this.maxWidth = options.maxWidth || 800;        this.maxHeight = options.maxHeight || 600;        this.maxSize = options.maxSize || 3 * 1024 * 1024;        this.input = options.input;        this.mime = { png: image/png, jpg: image/jpeg, jpeg: image/jpeg, bmp: image/bmp };        this.callback = options.callback || function () { };        this.loading = options.loading || function () { };        this._addEvent();    };    /**    * @description 绑定事件    * @param {Object} elm 元素    * @param {Function} fn 绑定函数    */    UploadPic.prototype._addEvent = function () {        var _this = this;        function tmpSelectFile(ev) {            _this._handelSelectFile(ev);        }        this.input.addEventListener(change, tmpSelectFile, false);    };    /**    * @description 绑定事件    * @param {Object} elm 元素    * @param {Function} fn 绑定函数    */    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);    };    /**    * @description 读取图片文件    * @param {Object} image 图片文件    */    UploadPic.prototype._readImage = function (file) {        var _this = this;        function tmpCreateImage(uri) {            _this._createImage(uri);        }        this.loading();        this._getURI(file, tmpCreateImage);    };    /**    * @description 通过文件获得URI    * @param {Object} file 文件    * @param {Function} callback 回调函数,返回文件对应URI    * return {Bool} 返回false    */    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,);                        //此处为自己加上的去掉base64不带“头”的判断            if (ret.indexOf(";base64,") >= 0) {                var num = ret.indexOf(";base64,");                num = parseInt(num) + 8;                _this.noHead = ret.substring(num);            }                        callback && callback(ret);        }        reader.onload = tmpLoad;        reader.readAsDataURL(file);        return false;    };    /**    * @description 创建图片    * @param {Object} image 图片文件    */    UploadPic.prototype._createImage = function (uri) {        var img = new Image();        var _this = this;        function tmpLoad() {            _this._drawImage(this);        }        img.onload = tmpLoad;        img.src = uri;    };    /**    * @description 创建Canvas将图片画至其中,并获得压缩后的文件    * @param {Object} img 图片文件    * @param {Number} width 图片最大宽度    * @param {Number} height 图片最大高度    * @param {Function} callback 回调函数,参数为图片base64编码    * return {Object} 返回压缩后的图片    */    UploadPic.prototype._drawImage = function (img, callback) {        //        this.sw = img.width;        //        this.sh = img.height;        //如果不需要压缩可将上面注释与下面的更换;        this.tw = img.width;        this.th = img.height;        this.sw = 100;        this.sh = 100;        this.scale = (this.tw / this.th).toFixed(2);        if (this.sw > this.maxWidth) {            this.sw = this.maxWidth;            this.sh = Math.round(this.sw / this.scale);        }        if (this.sh > this.maxHeight) {            this.sh = this.maxHeight;            this.sw = Math.round(this.sh * this.scale);        }        this.canvas = document.createElement(canvas);        var ctx = this.canvas.getContext(2d);        this.canvas.width = this.sw;        this.canvas.height = this.sh;        ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, this.sw, this.sh);        this.callback(this.canvas.toDataURL(this.type));        ctx.clearRect(0, 0, this.tw, this.th);        this.canvas.width = 0;        this.canvas.height = 0;        this.canvas = null;    };    function upload() {        var base64 = $("#base64").val();        var name = $("#name").text();        $.ajax(        {            type: "post",            url: "Upload.ashx",            data: {                base64: base64,                name: name            },            async: true,            success: function fun(rt) {                alert(rt);            }        });    };</script></body></html>
      • 然后是后台C#代码,就是简单的转格式,保存文件那些。
<%@ WebHandler Language="C#" Class="Upload" %>using System;using System.Web;using System.Drawing;using System.Drawing.Imaging;using System.IO;public class Upload : IHttpHandler {        public void ProcessRequest (HttpContext context) {        context.Response.ContentType = "text/plain";        string base64 = context.Request["base64"];        string name = context.Request["name"];        byte[] arr = Convert.FromBase64String(base64);        MemoryStream ms = new MemoryStream(arr);        Bitmap bmp = new Bitmap(ms);        //要保存的目录路径        string filePath = "Upimages";        filePath = context.Server.MapPath(filePath + "/" + name);        //bmp.Save("Upimages/"+name + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);        bmp.Save(filePath);        ms.Close();        context.Response.Write("true");    }     public bool IsReusable {        get {            return false;        }    }}

 

好啦,第一次写博客就是写到这里,不知道还有什么写差了的,希望各位提下建议,不喜可喷哈~

2014.08.22

解决微信内嵌浏览器无法响应上传文件(图片)的思路(2种办法)