码迷,mamicode.com
首页 > 微信 > 详细

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

时间:2014-08-22 22:19:49      阅读:563      评论:0      收藏:0      [点我收藏+]

标签:des   android   style   blog   http   color   java   os   使用   

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

 

      先自我介绍下,我是惠州学院大二(准大三)的学生,在去年暑假时候加了一个工作室开始写代码,也是在那个时候加入博客园,最近因为工作室要做微信端网页的开发需要一个上传图片功能,然后编码的时候发现用平时的方法做的上传功能在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="‘ + 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="‘ + 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 = 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 = !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 = " + data);
              //eval("data = \" " + 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里边都会显示我先一般处理程序发送的请求,而且能够看到返回的信息。我觉得这就是为何这个插件能够实现上传的原因。具体的原理我不知道怎么去解释这个原理,还请大牛帮忙,谢谢!

          bubuko.com,布布扣

通过这个实现了之后,腾讯“阉割”掉的应该不是<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=" + 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种办法)

标签:des   android   style   blog   http   color   java   os   使用   

原文地址:http://www.cnblogs.com/cai-rd/p/3930227.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!