ホームページ  >  記事  >  WeChat アプレット  >  PHP WeChat パブリック アカウント開発で遭遇する 5 つの落とし穴のまとめ

PHP WeChat パブリック アカウント開発で遭遇する 5 つの落とし穴のまとめ

高洛峰
高洛峰オリジナル
2017-03-21 15:54:353057ブラウズ

この記事では、主に PHP WeChat パブリック アカウント開発の 5 つの落とし穴について詳しく紹介します。興味のある方は参考にしてください。

メニューの返信は、返された XML ファイルに基づいて処理する必要があります。 WeChat では、WeChat 公式アカウントに関連する各 WeChat ユーザーの一意の識別子を取得できます。 WeChat パブリック プラットフォームの仕組みは、固定形式の XML ファイルを自分たちで出力し、WeChat APP がそれを解析して必要な情報を取得し、情報を統一的に処理するという単純なものです。 6 番目の落とし穴、WeChat 文書を見たら、上の写真に示すように、間違いなく殺されます。ここでの ToUserName と FromUserName は明確に区別する必要があります。WeChat ではユーザーが A→B であるため、ここでは明確に区別する必要があるようです。

PHP WeChat パブリック アカウント開発で遭遇する 5 つの落とし穴のまとめ

/// <summary>
 /// 接收微信发送的XML消息并且解析
 /// </summary>
 private void ReceiveXml()
 {
 try
 {
  Stream requestStream = System.Web.HttpContext.Current.Request.InputStream;
  byte[] requestByte = new byte[requestStream.Length];
  requestStream.Read(requestByte, 0, (int)requestStream.Length);
  string requestStr = Encoding.UTF8.GetString(requestByte);

  if (!string.IsNullOrEmpty(requestStr))
  { 

  //封装请求类
  XmlDocument requestDocXml = new XmlDocument();
  requestDocXml.LoadXml(requestStr);
  XmlElement rootElement = requestDocXml.DocumentElement;
  WxXmlModel WxXmlModel = new WxXmlModel();
  if (rootElement != null)
  {
   WxXmlModel.ToUserName = rootElement.SelectSingleNode("ToUserName") == null ? "" : rootElement.SelectSingleNode("ToUserName").InnerText;
   WxXmlModel.FromUserName = rootElement.SelectSingleNode("FromUserName") == null ? "" : rootElement.SelectSingleNode("FromUserName").InnerText;
   WxXmlModel.CreateTime = rootElement.SelectSingleNode("CreateTime") == null ? "" : rootElement.SelectSingleNode("CreateTime").InnerText;
   WxXmlModel.MsgType = rootElement.SelectSingleNode("MsgType") == null ? "" : rootElement.SelectSingleNode("MsgType").InnerText;
   switch (WxXmlModel.MsgType)
   {

   case "text"://文本
    WxXmlModel.Content = rootElement.SelectSingleNode("Content") == null ? "" : rootElement.SelectSingleNode("Content").InnerText;
    break;
   case "image"://图片
    WxXmlModel.PicUrl = rootElement.SelectSingleNode("PicUrl") == null ? "" : rootElement.SelectSingleNode("PicUrl").InnerText;
    break;
   case "event"://事件
    WxXmlModel.Event = rootElement.SelectSingleNode("Event") == null ? "" : rootElement.SelectSingleNode("Event").InnerText;
    if (WxXmlModel.Event != "TEMPLATESENDJOBFINISH")//关注类型
    {
    WxXmlModel.EventKey = rootElement.SelectSingleNode("EventKey") == null ? "" : rootElement.SelectSingleNode("EventKey").InnerText;
    }
    break;
   default:
    break;
   }
  }
  ResponseXML(WxXmlModel);//回复消息
  }

 

 }
 catch (Exception ee)
 {
  //记录错误日志
 }
 }

 /// <summary>
 /// 回复消息
 /// </summary>
 /// <param name="WxXmlModel"></param>
 private void ResponseXML(WxXmlModel WxXmlModel)
 {
 string XML = "";
 switch (WxXmlModel.MsgType)
 {
  case "text"://文本回复
  var info = oauth.GetUserInfo(Tools.WA_GetAccess_Token.IsExistAccess_Token(), WxXmlModel.FromUserName);
  Tools.WAEntity.OAuthUser user = Tools.JsonHelper.ParseFromJson<Tools.WAEntity.OAuthUser>(info);
  var content = WxXmlModel.Content.ToUpper();
  string NcbActUrl = ConfigurationManager.AppSettings["NcbActUrl"];
  string appid = ConfigurationManager.AppSettings["AppID"];
  if (content.Contains("T"))//接受的文字如果包含T
  {
   //业务处理
  }
  else
  {
   XML = ResponseMessage.ReText(WxXmlModel.FromUserName, WxXmlModel.ToUserName, "/:rose农场大数据欢迎你!/:rose");

  }
  break;
  case "event":
  switch (WxXmlModel.Event.ToLower())
  {
   case "subscribe":
   if (string.IsNullOrEmpty(WxXmlModel.EventKey))
   {
    XML = ResponseMessage.ReText(WxXmlModel.FromUserName, WxXmlModel.ToUserName, "关注成功!/:rose");

   }
   else
   {
    XML = ResponseMessage.SubScanQrcode(WxXmlModel.FromUserName, WxXmlModel.ToUserName, WxXmlModel.EventKey);//扫描带参数二维码先关注后推送事件
   }
   break;
   case "scan":
   XML = ResponseMessage.ScanQrcode(WxXmlModel.FromUserName, WxXmlModel.ToUserName, WxXmlModel.EventKey);//扫描带参数二维码已关注 直接推送事件
   break;
   case "click"://处理单击事件
   if (WxXmlModel.EventKey == "p1")
   {
    //自己的业务逻辑
   }
   else
   {
    //自己的业务逻辑
   }
   break;
   case "unsubscribe"://取消关注
   break;
  }
  break;
  default://默认回复
  break;
 }
 Response.Write(XML);//输出组织的XML信息

 }

これはメニューの情報処理です。真実を知らない人は、いわゆるResponseMessageが何を意味するのかを尋ねるようです。OK、私はもうWeChatパブリックプラットフォームについて文句を言うことはできません。この 3 日間で何かを調べました。


public class ResponseMessage

{

 #region 接收的类型
 /// <summary>
 /// 接收文本
 /// </summary>
 /// <param name="FromUserName"></param>
 /// <param name="ToUserName"></param>
 /// <param name="Content"></param>
 /// <returns></returns>
 public static string GetTextTest(string FromUserName, string ToUserName, string Content, string key)
 {
 CommonMethod.WriteTxt(Content);//接收的文本消息
 string XML = "";
 switch (Content)
 {
  case "关键字":
  XML = ReText(FromUserName, ToUserName, "关键词回复测试——兴农丰华:" + key);
  break;
  case "单图文":
  XML = ReArticle(FromUserName, ToUserName, "测试标题", "测试详情——兴农丰华:" + key, "http://www.xnfhtech.com/templets/boze/images/20120130083143544.gif", "http://www.xnfhtech.com/");
  break;
  default:
  XML = ReText(FromUserName, ToUserName, "无对应关键字——兴农丰华:" + key);
  break;
 }
 return XML;

 }

 /// <summary>
 /// 未关注扫描带参数二维码
 /// </summary>
 /// <param name="FromUserName"></param>
 /// <param name="ToUserName"></param>
 /// <param name="EventKey"></param>
 /// <returns></returns>
 public static string SubScanQrcode(string FromUserName, string ToUserName, string EventKey)
 {
 return "";
 }
 

 /// <summary>
 /// 已关注扫描带参数二维码
 /// </summary>
 /// <param name="FromUserName"></param>
 /// <param name="ToUserName"></param>
 /// <param name="EventKey"></param>
 /// <returns></returns>
 public static string ScanQrcode(string FromUserName, string ToUserName, string EventKey)
 {
 return "";
 }
 #endregion
 

 #region 回复方式
 /// <summary>
 /// 回复文本
 /// </summary>
 /// <param name="FromUserName">发送给谁(openid)</param>
 /// <param name="ToUserName">来自谁(公众账号ID)</param>
 /// <param name="Content">回复类型文本</param>
 /// <returns>拼凑的XML</returns>

 public static string ReText(string FromUserName, string ToUserName, string Content)
 {
 string XML = "<xml><ToUserName><![CDATA[" + FromUserName + "]]></ToUserName><FromUserName><![CDATA[" + ToUserName + "]]></FromUserName>";//发送给谁(openid),来自谁(公众账号ID)
 XML += "<CreateTime>" + CommonMethod.ConvertDateTimeInt(DateTime.Now) + "</CreateTime>";//回复时间戳
 XML += "<MsgType><![CDATA[text]]></MsgType>";//回复类型文本
 XML += "<Content><![CDATA[" + Content + "]]></Content><FuncFlag>0</FuncFlag></xml>";//回复内容 FuncFlag设置为1的时候,自动星标刚才接收到的消息,适合活动统计使用
 return XML;

 }
 

 /// <summary>
 /// 回复单图文
 /// </summary>
 /// <param name="FromUserName">发送给谁(openid)</param>
 /// <param name="ToUserName">来自谁(公众账号ID)</param>
 /// <param name="Title">标题</param>
 /// <param name="Description">详情</param>
 /// <param name="PicUrl">图片地址</param>
 /// <param name="Url">地址</param>
 /// <returns>拼凑的XML</returns>

 public static string ReArticle(string FromUserName, string ToUserName, string Title, string Description, string PicUrl, string Url)
 {

 string XML = "<xml><ToUserName><![CDATA[" + FromUserName + "]]></ToUserName><FromUserName><![CDATA[" + ToUserName + "]]></FromUserName>";//发送给谁(openid),来自谁(公众账号ID)
 XML += "<CreateTime>" + CommonMethod.ConvertDateTimeInt(DateTime.Now) + "</CreateTime>";//回复时间戳
 XML += "<MsgType><![CDATA[news]]></MsgType><Content><![CDATA[]]></Content><ArticleCount>1</ArticleCount><Articles>";
 XML += "<item><Title><![CDATA[" + Title + "]]></Title><Description><![CDATA[" + Description + "]]></Description><PicUrl><![CDATA[" + PicUrl + "]]></PicUrl><Url><![CDATA[" + Url + "]]></Url></item>";
 XML += "</Articles><FuncFlag>0</FuncFlag></xml>";
 return XML;

 }


 /// <summary>
 /// 多图文回复
 /// </summary>
 /// <param name="FromUserName">发送给谁(openid)</param>
 /// <param name="ToUserName">来自谁(公众账号ID)</param>
 /// <param name="ArticleCount">图文数量</param>
 /// <param name="dtArticle"></param>
 /// <returns></returns>

 public static string ReArticle(string FromUserName, string ToUserName, int ArticleCount, System.Data.DataTable dtArticle)
 {

 string XML = "<xml><ToUserName><![CDATA[" + FromUserName + "]]></ToUserName><FromUserName><![CDATA[" + ToUserName + "]]></FromUserName>";//发送给谁(openid),来自谁(公众账号ID)
 XML += "<CreateTime>" + CommonMethod.ConvertDateTimeInt(DateTime.Now) + "</CreateTime>";//回复时间戳
 XML += "<MsgType><![CDATA[news]]></MsgType><Content><![CDATA[]]></Content><ArticleCount>" + ArticleCount + "</ArticleCount><Articles>";
 foreach (System.Data.DataRow Item in dtArticle.Rows)

 {
  XML += "<item><Title><![CDATA[" + Item["Title"] + "]]></Title><Description><![CDATA[" + Item["Description"] + "]]></Description><PicUrl><![CDATA[" + Item["PicUrl"] + "]]></PicUrl><Url><![CDATA[" + Item["Url"] + "]]></Url></item>";
 }
 XML += "</Articles><FuncFlag>0</FuncFlag></xml>";
 return XML;

 }

 #endregion

 }

OK、独自のロジック コードで、返信は完全に実装されていますか?


7番目の穴、
本当にもう数えたくないのですが、この返信でよろしいでしょうか?正直に言うと、よくわかりません。なぜなら、それを書いた後、それをどこに呼び出すか知っていますか? サーバーの検証に合格した後に返信を追加するのが最も安全です。私にはもうモラルがありません。

次に、ユーザー情報の取得について話しましょう。なぜなら、私たちのものは一般的に H5 ページに基づいているからです。したがって、以前に設定した


を使用する必要があります。実際、以前のものと比較すると、これには落とし穴が少なくともはるかに少なくなります。当面は。コードを入れてみましょう。

PHP WeChat パブリック アカウント開発で遭遇する 5 つの落とし穴のまとめ

//微信网页授权2.0
public class Oauth2
{
 JavaScriptSerializer Jss = new JavaScriptSerializer();
 public Oauth2() { }
 

 /// <summary>
 /// 对页面是否要用授权
 /// </summary>
 /// <param name="Appid">微信应用id</param>
 /// <param name="redirect_uri">回调页面</param>
 /// <param name="scope">应用授权作用域snsapi_userinfo(不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)</param>
 /// <returns>授权地址</returns>

 public string GetCodeUrl(string Appid, string redirect_uri, string scope)
 {
 return string.Format("https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type=code&scope={2}&state=STATE#wechat_redirect", Appid, redirect_uri, scope);
 }

 

 /// <summary>
 /// 对页面是否要用授权
 /// </summary>
 /// <param name="Appid">微信应用id</param>
 /// <param name="redirect_uri">回调页面</param>
 /// <param name="scope">应用授权作用域snsapi_userinfo(不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)</param>
 /// <returns>授权地址</returns>
 public string GetCodeUrl(string Appid, string redirect_uri, string scope,string state)
 {
 return string.Format("https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type=code&scope={2}&state={3}#wechat_redirect", Appid, redirect_uri, scope, state);
 }

 

 /// <summary>
 /// 用code换取openid 此方法一般是不获取用户昵称时候使用
 /// </summary>
 /// <param name="Appid"></param>
 /// <param name="Appsecret"></param>
 /// <param name="Code">回调页面带的code参数</param>
 /// <returns>微信用户唯一标识openid</returns>
 public string CodeGetOpenid(string Appid, string Appsecret, string Code)
 {
 string url = string.Format("https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code", Appid, Appsecret, Code);
 string ReText = CommonMethod.WebRequestPostOrGet(url, "");//post/get方法获取信息
 Dictionary<string, object> DicText = (Dictionary<string, object>)Jss.DeserializeObject(ReText);
 if (!DicText.ContainsKey("openid"))
  return "";
 return DicText["openid"].ToString();
 }

 

 /// <summary>
 ///用code换取获取用户信息(包括非关注用户的)
 /// </summary>
 /// <param name="Appid"></param>
 /// <param name="Appsecret"></param>
 /// <param name="Code">回调页面带的code参数</param>
 /// <returns>获取用户信息(json格式)</returns>

 public string GetUserInfo(string Appid, string Appsecret, string Code)
 {

 string url = string.Format("https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code", Appid, Appsecret, Code);
 string ReText = CommonMethod.WebRequestPostOrGet(url, "");//post/get方法获取信息
 Dictionary<string, object> DicText = (Dictionary<string, object>)Jss.DeserializeObject(ReText);
 if (!DicText.ContainsKey("openid"))

 {

  log.Error("获取openid失败,错误码:" + DicText["errcode"].ToString());

 return "";

 }
 else

 {

  return CommonMethod.WebRequestPostOrGet("https://api.weixin.qq.com/sns/userinfo?access_token=" + DicText["access_token"] + "&openid=" + DicText["openid"] + "&lang=zh_CN", "");

 }

 }

 

 

 /// <summary>
 /// 通过openId获取用户信息
 /// </summary>
 /// <param name="accesstoken"></param>
 /// <param name="openid"></param>
 /// <returns></returns>
 public string GetUserInfo(string accesstoken, string openid)

 {
 string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info?access_token={0}&openid={1}&lang=zh_CN", accesstoken, openid);
 return CommonMethod.WebRequestPostOrGet(url, "");//post/get方法获取信息

 }

}

それを呼び出す必要がある場合、内部のメソッドを直接使用して WeChat Web ページの承認を取得できます。たとえば、コントローラー A の下でビュー B の承認を取得し、ユーザーに関する関連情報を取得したい場合は、次のようにします。はい、GetCodeUrl(appid, "http://" + Url + "/A/B", "snsapi_userinfo") のように直接呼び出すことができます


ここで文句を言ったほうがいいでしょう。


8番目の穴、
WeChatメニューJSONのURLスプライシング、前にjs検証がないので、一体、http://を素直に追加するだけです。

しかし、ここで認証後、多くのユーザー情報を使用する必要があるため、これはプロジェクトでSessionを使用し、Sessionに値がある場合は直接パブリックメソッドを記述します。 、次に値を直接取得します。内部の一部については、すべてのコードを投稿する必要があるわけではないことを説明したいと思います。ここにあるコードは、私が個人的に投稿する必要があると考えているものだけです。表示できない方法がある場合がありますので、必要に応じてこのページにメッセージを残してください。

public string getSession()

{

 log.Error("GetSession");
 string oauthStr = "";
 try

 {
 if (Session != null && (Session["oauthStr"] == null || string.IsNullOrEmpty(Session["oauthStr"].ToString())))
 {
  if (!string.IsNullOrEmpty(Request.QueryString["code"]))
  {
  Oauth2 oauth = new Oauth2();
  string code = Convert.ToString(Request["code"]);
  oauthStr = oauth.GetUserInfo(ConfigurationManager.AppSettings["AppID"],
   ConfigurationManager.AppSettings["AppSecret"], code);
   Session["oauthStr"] = oauthStr;
  Tools.WAEntity.OAuthUser oAuthUser = new Tools.WAEntity.OAuthUser();
  oAuthUser = Tools.JsonHelper.ParseFromJson<Tools.WAEntity.OAuthUser>(oauthStr);
  }
  return oauthStr;

 }
 else

 {

  Tools.WAEntity.OAuthUser oAuthUser = new Tools.WAEntity.OAuthUser();
  oAuthUser = Tools.JsonHelper.ParseFromJson<Tools.WAEntity.OAuthUser>(Session["oauthStr"].ToString());
  return Session["oauthStr"].ToString();

 }

 }

 catch (Exception e) { log.Error(e.ToString()); return oauthStr; };

}

それから、情報を取得する必要があるページに遭遇するたびに、通常はこれを呼び出すだけです。


基本的に、残りは私たち自身で対処しなければならないビジネスロジックです。落とし穴について話を続けましょう。


9 番目の落とし穴、
WeChat への写真のアップロードは、決してあなただけのものではありません。あなたが信じても信じなくても、私はこの赤ちゃんを本当に信じています。特別な画像を for ループでアップロードすることはできません。もちろんこれはAppleモデルのみで、Androidでも問題ありません。

先ほど JS のセキュリティ検証の問題について触れましたが、ここではこれらの検証を呼び出し、適切な権限を要求し、画像情報などを取得します。



心配しないでください、今赤ちゃんが上の写真について話しているのは、写真の中の弟のことではありません。 。 。 。 。

PHP WeChat パブリック アカウント開発で遭遇する 5 つの落とし穴のまとめ

引き続き戻ってコードを見てみましょう。


まず Json



public class JsApi

{
 JavaScriptSerializer Jss = new JavaScriptSerializer(); 

 public JsApi() { } 
 const string URL_FORMAT_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi";

 #region 验证JsApi权限配置
 /// <summary>
 /// 获取JsApi权限配置的数组/四个参数
 /// </summary>
 /// <param name="Appid">应用id</param>
 /// <param name="Appsecret">密钥</param>
 /// <returns>json格式的四个参数</returns>
 public string GetJsApiInfo(string Appid, string Appsecret)
 {
 string jsapi_ticket = "";

 //ticket 缓存7200秒

 if (System.Web.HttpContext.Current.Session["jsapi_ticket"] == null)

 {
  string ticketurl = string.Format(URL_FORMAT_TICKET, BasicApi.GetAccessToken(Appid, Appsecret));//"https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + GetAccessToken(Appid, Appsecret) + "&type=jsapi"
  jsapi_ticket = CommonMethod.WebRequestPostOrGet(ticketurl, "");//BasicApi.GetTokenSession
  System.Web.HttpContext.Current.Session["jsapi_ticket"] = jsapi_ticket;
  System.Web.HttpContext.Current.Session.Timeout = 7200;
  BasicApi.WriteTxt("jsapi_ticket1:" + jsapi_ticket);


 }
 else
 {
  jsapi_ticket = System.Web.HttpContext.Current.Session["jsapi_ticket"].ToString();
  BasicApi.WriteTxt("jsapi_ticket2:" + jsapi_ticket);
 } 

 Dictionary<string, object> respDic = (Dictionary<string, object>)Jss.DeserializeObject(jsapi_ticket);
 jsapi_ticket = respDic["ticket"].ToString();//获取ticket
 string timestamp = CommonMethod.ConvertDateTimeInt(DateTime.Now).ToString();//生成签名的时间戳
 string nonceStr = CommonMethod.GetRandCode(16);//生成签名的随机串
 string url = System.Web.HttpContext.Current.Request.Url.AbsoluteUri.ToString();//当前的地址
 BasicApi.WriteTxt("url:" + url);
 string[] ArrayList = { "jsapi_ticket=" + jsapi_ticket, "timestamp=" + timestamp, "noncestr=" + nonceStr, "url=" + url };
 Array.Sort(ArrayList);
 string signature = string.Join("&", ArrayList);
 signature = FormsAuthentication.HashPasswordForStoringInConfigFile(signature, "SHA1").ToLower();
 string r = "{\"appId\":\"" + Appid + "\",\"timestamp\":" + timestamp + ",\"nonceStr\":\"" + nonceStr +
   "\",\"signature\":\"" + signature +
   "\",\"jsApiList\":[\"chooseImage\",\"previewImage\",\"uploadImage\",\"downloadImage\",\"scanQRCode\",\"onMenuShareQQ\"]}";
 BasicApi.WriteTxt("r:" + r.Replace(" ", ""));
 return r.Replace(" ", "");

 }

}

を処理してから、具体的な呼び出しを見てみましょう。


バックグラウンドコードは実際には非常に単純で、構成ファイルを直接出力し、フロントエンド js を直接呼び出すだけです。


JsApi jsApi = new JsApi();
string config = jsApi.GetJsApiInfo(appId, appSecret);
ViewBag.config = config;

前台代码,其实也不难,这个有官方的例子的。 


<script type="text/javascript">
 wx.config(@Html.Raw(ViewBag.config));//后台传递的微信配置文件
 wx.ready(function () {
 $("#avatar").click(function () {
  wx.chooseImage({
  count: 1, // 图片数量 默认9
  sizeType: [&#39;compressed&#39;], // 可以指定是原图还是压缩图,默认二者都有&#39;original&#39;,
  sourceType: [&#39;album&#39;, &#39;camera&#39;], // 可以指定来源是相册还是相机,默认二者都有
  success: function (res) {
   var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
   wx.uploadImage({
   localId: &#39;&#39; + localIds,
   isShowProgressTips: 1,
   success: function (res) {
    serverId = res.serverId;
    getWxPhoto(serverId);

   }

   });

  }

  });

 });

 });

 wx.error(function (res) {
 alert("接口验证失败,详细信息:\n" + JSON.stringify(res));
 });
 var types = 1;
 function getWxPhoto(mediaId) {
 $.ajax({
  async: false,
  type: "post",
  url: "/ActivityRegistration/DownloadWxPhoto",//自己的处理方法
  data: { mediaId: mediaId, types: types },
  success: function (data) {
  $("#imageico").val(data.result);
  $("#hed_pic").attr(&#39;src&#39;, ".." + data.result);
  $("#hed_pic").attr(&#39;alt&#39;, "avatarImg");

  }

 });

 }

</script>

OK,后台方法其实也很简单,就是一个二进制文件处理,不对,简单个蛋蛋,特么的,因为路径的问题,坑了宝宝一个小时,特么的。还有这里建议,等微信图片下载完成之后再给前台加载图片,保证每一个图片都加载完成,保证后台的图片的上传完成。 


/// <summary>
/// 下载多媒体文件
/// </summary>
/// <param name="userName">公众号</param>
/// <param name="mediaId">媒体ID</param>
/// <param name="data">返回下载是否成功</param>
/// <param name="types">添加的图片类型</param>
/// <returns>返回多媒体文件数据;如果下载失败,返回null。</returns>
public JsonResult DownloadWxPhoto(string mediaId, int types)

{

 ErrorMessage errorMessage;
 string access_token = BasicApi.GetAccessToken(ConfigurationManager.AppSettings["AppID"], ConfigurationManager.AppSettings["AppSecret"]);
 byte[] data = MediaHelper.Download(access_token, mediaId, out errorMessage);
 string files = String.Empty, fileName = String.Empty;
 files = Server.MapPath("~/Wxinphoto/");
 if (!Directory.Exists(files))
 {
 Directory.CreateDirectory(files);
 }
 fileName = files + DateTime.Now.Ticks + ".jpg";
 if (data != null)
 {
 bool flag = writeFile(data, fileName);
 if (flag)
 {
  errorMessage = new ErrorMessage(ErrorMessage.SuccessCode, "下载多媒体文件成功。");
 }
 else
 {
  errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "从微信服务器下载多媒体文件失败。");
 }

 }
 else
 errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "从微信服务器下载多媒体文件失败。");
 return Json(new { result = "/" + urlconvertor(fileName), errorMessage = errorMessage });

}


//读filename到byte[] 
private byte[] ReadFile(string fileName)
{
 FileStream pFileStream = null;
 byte[] pReadByte = new byte[0];
 try
 {
 pFileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
 BinaryReader r = new BinaryReader(pFileStream);
 r.BaseStream.Seek(0, SeekOrigin.Begin); //将文件指针设置到文件开
 pReadByte = r.ReadBytes((int)r.BaseStream.Length);
 return pReadByte;
 }
 catch
 {
 return pReadByte;
 }
 finally
 {
 if (pFileStream != null)
  pFileStream.Close();

 }

}

 

//写byte[]到fileName

private bool writeFile(byte[] pReadByte, string fileName)

{

 FileStream pFileStream = null;

 try

 {
 pFileStream = new FileStream(fileName, FileMode.OpenOrCreate);
 pFileStream.Write(pReadByte, 0, pReadByte.Length);
 }
 catch

 {
 return false;
 }
 finally

 {
 if (pFileStream != null)

  pFileStream.Close();

 }

 return true;

}
/// <summary>
/// 判断目标字节数组是否位于源字节数组的开始
/// </summary>
/// <param name="source">源字节数组</param>
/// <param name="target">目标字节数组</param>
/// <returns>返回目标字节数组是否位于源字节数组的开始</returns>

private bool StartsWithBytes(byte[] source, byte[] target)

{

 if (source == null && target == null)

 return true;

 if (source == null && target != null || source != null && target == null)
 return false;
 if (source.Length < target.Length)
 return false;
 bool startsWith = true;
 for (int i = 0; i < target.Length; i++)
 {
 if (source[i] != target[i])

 {
  startsWith = false;

  break;

 }

 }

 return startsWith;

}

 是不是以为这就算完事了,我的乖乖,头像上传了,微信摄像头也特么该调用的调用了,宝宝好幸福,宝宝也是牛人一个了,记住前面的东东,宝宝还没有说坑呢。
来重复我们的第九个坑,特么的,你JS写个for循环要是能循环把图片上传到后台,宝宝也服气,真的,宝宝服气。 

直接说吧,最后我自己想了下,也和队友讨论了下,可能是因为微信有什么验证,导致之后一张图片上传成功之后,才能进行一张,但是我们Iphone就是特么的特例,大Android没用问题的,就是Iphone有了问题,而且问题不小,上传四张图片吧,老特么是最后一张,最后,找到了万能的网友,感谢你,不过宝宝已经忘记了在哪里找到的了,尴尬了。。。。。。。。。。。 


<script type="text/javascript">

 var types = 2;

 var urlList="";

 var i = 0;

 function up(resurl) {

  if (i < resurl.localIds.length) {

  // 上传照片resu.localIds[i]
  wx.uploadImage({
   localId: &#39;&#39; + resurl.localIds[i],
   isShowProgressTips: 1,
   success: function (res) {
   // alert("res.serverId:" + res.serverId);
   mediaId = res.serverId;
   $.ajax({
    async: false,
    type: "post",
    url: "/ActivityRegistration/DownloadWxPhoto",
    data: { mediaId: mediaId, types: types },
    success: function (data) {
    $("#picPath").append(&#39;<li><p class="imgbox"><img src="/img/cechanadd.png" id="picture&#39; + i + &#39;" alt="" /></p></li>&#39;);

    $("#picture" + i).attr(&#39;src&#39;, data.result);
    $("#picPath").append(&#39;<input value=&#39; + data.result + &#39; type="hidden" id="picurl&#39; + i + &#39;" class="picclass" />&#39;);

    i++;

    if (i == resurl.localIds.length - 1) {

     $("#picPath").append(&#39;<li><p class="imgbox"><img src="/img/cechanadd.png" id="picture" alt="" /></p></li>&#39;);

    }

    up(resurl);
    }
   });

   }
  });
  } else {

  i = 0;
  }

 }

 

 
 //上传图片
 wx.config(@Html.Raw(ViewBag.config));
 wx.ready(function () {
  $("#picPath").click(function () {
  wx.chooseImage({
   count: 3, // 默认9
   sizeType: [&#39;compressed&#39;], // 可以指定是原图还是压缩图,默认二者都有&#39;original&#39;,
   sourceType: [&#39;album&#39;, &#39;camera&#39;], // 可以指定来源是相册还是相机,默认二者都有
   success: function (resu) {
   var localIds = resu.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
   if (localIds.indexOf("wxlocalresource") != -1) {    
   localIds = localIds.replace("wxlocalresource", "wxLocalResource");
   }
 
   @(index += 1)
   if (localIds != &#39;&#39;) {
    $("#picPath").html("");
   var sear = new RegExp(&#39;,&#39;);
    if (sear.test(localIds)) {
    up(resu);
    }

    else {
    $("#picPath").append(&#39; <li><p class="imgbox"><img src="/img/cechanadd.png" id="picture&#39; + &#39;@index&#39; + &#39;" alt="" " /></p></li>&#39;);
    $("#picture" + "@index").attr(&#39;src&#39;, localIds);
    // 上传照片
    wx.uploadImage({
     localId: &#39;&#39; + localIds,

    isShowProgressTips: 1,
     success: function (res) {
     mediaId = res.serverId;

     $.ajax({
      async: false,

      type: "post",
      url: "/ActivityRegistration/DownloadWxPhoto",

      data: { mediaId: mediaId, types: types },

      success: function (data) {

      $("#picPath").append(&#39;<input value=&#39; + data.result + &#39; type="hidden" id="picurl&#39; + @index + &#39;" class="picclass" />&#39;);

      $("#picPath").append(&#39;<li><p class="imgbox"><img src="/img/cechanadd.png" id="picture" alt="" /></p></li>&#39;);
      }
     });

    }
    });
    }
    // $("#picPath").append(&#39;<li><p class="imgbox"><img src="/img/cechanadd.png" id="picture" alt="" /></p></li>&#39;);

   }
   }
  });
  });
 });
 wx.error(function (res) {
  alert("接口验证失败,详细信息:\n" + JSON.stringify(res));
 });
 </script>

请记住,递归就特么可以了。

说到这里,宝宝已经不想多说什么了,特么的产品你能不能不装逼,你特么见过那个微信能回复一个信息直接跳转网页的,你咋不去屎呢,联想到前几天大阿里的月饼时间,突然感觉我们程序员挺悲剧的,成功的都是特么的产品,然后出问题的都是我们程序员的锅?试问一下,这个锅真心我们程序员该背么。 

算了,还是不吐槽了,已经无力了。。。。宝宝92年降临,现在确实82年的皮肤呀,唉,宝宝累了,真的。 

顺便给点H5页面的建议吧。比如当点击返回键的时候,我们需要刷新页面的时候,就是所谓的判断页面要不要刷新,这里有很多种方法,但是微信里面宝宝还是觉得这么干靠谱。 


<script type="text/javascript">
 if (window.name != "hasLoad") {
 location.reload();
 window.name = "hasLoad";
 } else {
 window.name = "";
 }
</script>

还有,那个微信执行完成之后想直接退出当前界面进入微信公众号界面的,直接调用微信的一个内置的方法即可。记得写到<script></script>里面。 

WeixinJSBridge.call('closeWindow'); //这是微信关闭当前网页

这么自信的以为自己搞定了所有,你跑呀,你要跑起来,嗯哼,别不服气。 

微信公众账号指第十坑,我自己加的,哈哈,就是前面的JS验证的时候,你不要头文件,怎么搞定这些事情,哈哈。是不是宝宝赢了。Oh  perfect,I like it。

以上がPHP WeChat パブリック アカウント開発で遭遇する 5 つの落とし穴のまとめの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。