.NET生成微信小程序推廣二維碼

前言

對於小程序大家可能都非常熟悉了,隨着小程序的不斷普及越來越多的公司都開始推廣使用起來了。今天接到一個需求就是生成小程序碼,並且與運營給的推廣圖片合併在一起做成一張漂亮美觀的推廣二維碼,掃碼這種二維碼就可以進入小程序。爲了節省服務器內存資源,我想的就是成功調用通微信生成小程序碼的接口後直接把微信返回過來的圖片二進制內容(返回的圖片 Buffer)轉化爲二進制byte[]文件流,然後再轉成Image這樣就不需要在保存到本地直接讀取本地的背景圖片通過GDI+(Graphics)繪製圖片。

選擇小程序碼生成方式

首先微信小程序官方文檔提供了三種生成小程序碼的方法,如下所示(本文采用的是第三種,需要的碼數量極多的業務場景):

文檔詳情地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getQRCode.html

1、createwxaqrcode獲取小程序二維碼,適用於需要的碼數量較少的業務場景。通過該接口生成的小程序碼,永久有效,有數量限制。

2、getwxacode獲取小程序碼,適用於需要的碼數量較少的業務場景。通過該接口生成的小程序碼,永久有效,有數量限制。

3、getwxacodeunlimit獲取小程序碼,適用於需要的碼數量極多的業務場景。通過該接口生成的小程序碼,永久有效,數量暫無限制。

獲取全局唯一後臺接口調用憑據

對接開發過微信相關的業務的同學應該都清楚,調用微信接口很多情況下都會需要使用到access_token接口調用憑證。一般來說access_token的有效時長爲2小時,爲了不頻繁調用該接口我們可以通過緩存的方法把調用憑證存起來並設置合理的過期時間(redis,cookie,memorycache都是非常不錯的選擇)。

文檔詳情地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html

請求接口

GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET 

請求參數

屬性類型必填說明
grant_type string 填寫 client_credential
appid string 小程序唯一憑證,即 AppID,可在「微信公衆平臺 - 設置 - 開發設置」頁中獲得。(需要已經成爲開發者,且賬號沒有異常狀態)
secret string 小程序唯一憑證密鑰,即 AppSecret,獲取方式同 appid

返回參數

屬性類型說明
access_token string 獲取到的憑證
expires_in number 憑證有效時間,單位:秒。目前是7200秒之內的值。

access_token 的存儲與更新

  • access_token 的存儲至少要保留 512 個字符空間;
  • access_token 的有效期目前爲 2 個小時,需定時刷新,重複獲取將導致上次獲取的 access_token 失效;
  • 建議開發者使用中控服務器統一獲取和刷新 access_token,其他業務邏輯服務器所使用的 access_token 均來自於該中控服務器,不應該各自去刷新,否則容易造成衝突,導致 access_token 覆蓋而影響業務;
  • access_token 的有效期通過返回的 expires_in 來傳達,目前是7200秒之內的值,中控服務器需要根據這個有效時間提前去刷新。在刷新過程中,中控服務器可對外繼續輸出的老 access_token,此時公衆平臺後臺會保證在5分鐘內,新老 access_token 都可用,這保證了第三方業務的平滑過渡;
  • access_token 的有效時間可能會在未來有調整,所以中控服務器不僅需要內部定時主動刷新,還需要提供被動刷新 access_token 的接口,這樣便於業務服務器在API調用獲知 access_token 已超時的情況下,可以觸發 access_token 的刷新流程。

詳情可參考微信公衆平臺文檔 《獲取access_token》

請求示例代碼

               /// <summary>
        /// 獲取小程序全局唯一後臺接口調用憑據(access_token)
        /// </summary>
        /// <returns></returns>
        public string GetWechatAccessToken()
        {
            var appId = "你的小程序AppID";//小程序唯一憑證,即 AppID
            var secret = "你的小程序AppSecret"; //小程序唯一憑證密鑰,即 AppSecret
            string Url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, secret);

            string Result = HttpWebRequest(Url, "GET", "", Encoding.UTF8);

            var obj = JsonConvert.DeserializeObject<AccessToken>(Result);

            if (obj != null && obj.access_token != null)
            {
                return obj.access_token;
            }
            else
            {
                return "";
            }
        }


        /// <summary>
        /// WebRequest網絡請求
        /// </summary>
        /// <param name="requestUrl">請求地址</param>
        /// <param name="method">請求方式(GET/POST)</param>
        /// <param name="data">請求參數(method="POST"需要攜帶)</param>
        /// <param name="encoding">字符編碼</param>
        /// <param name="contentType">請求數據的內容類型</param>
        /// <returns></returns>
        public string HttpWebRequest(string requestUrl, string method, string data, Encoding encoding,string contentType="application/json;charset=UTF-8")
        {
            WebRequest webRequest = WebRequest.Create(requestUrl);
            webRequest.Method = method;
            if (method == "POST")
            {
                byte[] bytes = Encoding.Default.GetBytes(data);
                webRequest.ContentType = contentType;
                webRequest.ContentLength = bytes.Length;
                Stream requestStream = webRequest.GetRequestStream();
                requestStream.Write(bytes, 0, bytes.Length);
                requestStream.Close();
            }

            WebResponse response = webRequest.GetResponse();
            Stream responseStream = response.GetResponseStream();
            if (responseStream == null)
            {
                return "";
            }

            StreamReader streamReader = new StreamReader(responseStream, encoding);
            string result = streamReader.ReadToEnd();
            responseStream.Close();
            streamReader.Close();
            return result;
        }



    /// <summary>
    /// 響應模型
    /// </summary>
    public class AccessToken
    {
        /// <summary>
        /// 獲取到的憑證
        /// </summary>
        public string access_token { get; set; }

        /// <summary>
        /// 憑證有效時間,單位:秒。目前是7200秒之內的值
        /// </summary>
        public int expires_in { get; set; }

        /// <summary>
        /// 錯誤碼
        /// </summary>
        public int errcode { get; set; }

        /// <summary>
        /// 錯誤信息
        /// </summary>
        public string errmsg { get; set; }
    }

小程序碼獲取

請求地址

POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN 

請求參數

屬性類型必填說明
  access_token string 接口調用憑證,該參數爲 URL 參數,非 Body 參數。使用getAccessToken 或者 authorizer_access_token
  scene string 最大32個可見字符,只支持數字,大小寫英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符請自行編碼爲合法字符(因不支持%,中文無法使用 urlencode 處理,請使用其他編碼方式)
  page string 默認是主頁,頁面 page,例如 pages/index/index,根路徑前不要填加 /,不能攜帶參數(參數請放在scene字段裏),如果不填寫這個字段,默認跳主頁面。scancode_time爲系統保留參數,不允許配置
  check_path bool 默認是true,檢查page 是否存在,爲 true 時 page 必須是已經發布的小程序存在的頁面(否則報錯);爲 false 時允許小程序未發佈或者 page 不存在, 但page 有數量上限(60000個)請勿濫用。
  env_version string 要打開的小程序版本。正式版爲 "release",體驗版爲 "trial",開發版爲 "develop"。默認是正式版。
  width number 默認430,二維碼的寬度,單位 px,最小 280px,最大 1280px
  auto_color bool 自動配置線條顏色,如果顏色依然是黑色,則說明不建議配置主色調,默認 false
  line_color object 默認是{"r":0,"g":0,"b":0} 。auto_color 爲 false 時生效,使用 rgb 設置顏色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十進制表示
  is_hyaline bool 默認是false,是否需要透明底色,爲 true 時,生成透明底色的小程序

返回參數

屬性類型說明
buffer buffer 圖片 Buffer
errcode number 錯誤碼
errmsg string 錯誤信息

接口請求成功會返回的圖片 Buffer(如果調用成功,會直接返回圖片二進制內容(圖片文件流),如果請求失敗,會返回 JSON 格式的數據。)

請求代碼

注意:這個與前面獲取授權憑證的網絡請求不同的是因爲要接收請求返回過來的圖片二進制內容(buffer),然後需要把二進制文件流轉化爲byte[]二進制字節流,然後在轉化Image。

               /// <summary>
        /// 獲取小程序碼圖片
        /// </summary>
        /// <param name="access_token">接口調用憑據</param>
        /// <param name="param">攜帶參數</param>
        private Image GetWetchatAppletQRCodeImage(string access_token, string param)
        {
            string requestData = "{\"scene\":\"" + param + "\"}";
            string requestUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + access_token;

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
            request.Method = "POST";
            request.ContentType = "application/json;charset=UTF-8";
            byte[] payload = System.Text.Encoding.UTF8.GetBytes(requestData);
            request.ContentLength = payload.Length;
            Stream writer = request.GetRequestStream();
            writer.Write(payload, 0, payload.Length);
            writer.Close();
            HttpWebResponse response;
            response = (HttpWebResponse)request.GetResponse();
            Stream stream = response.GetResponseStream();//獲取返回的圖片 Buffer(文件流)
            byte[] imageBuffer = StreamToBytes(stream);

            return ByteArrayConvertToImage(imageBuffer);
        }

        /// <summary>
        /// 將文件數據流轉爲二進制byte[]字節流
        /// </summary>
        /// <param name="stream">文件流</param>
        /// <returns></returns>
        private byte[] StreamToBytes(Stream stream)
        {
            List<byte> bytes = new List<byte>();
            int temp = stream.ReadByte();
            while (temp != -1)
            {
                bytes.Add((byte)temp);
                temp = stream.ReadByte();
            }
            return bytes.ToArray();
        }


        /// <summary>
        /// byte [] 轉化爲Iamge
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public static Image ByteArrayConvertToImage(byte[] buffer)
        {
            using (MemoryStream ms = new MemoryStream(buffer))
            {
                // 直接調用Image庫類中自帶的方法使用MemoryStream實例對象獲取Image
                return Image.FromStream(ms);
            }
        }

小程序碼和背景圖合併

               /// <summary>
        /// 小程序推廣二維碼獲取
        /// </summary>
        /// <param name="userId">小程序碼攜帶的用戶參數</param>
        /// <returns></returns>
        public JsonResult GetCompositePictureUrl(int userId)
        {
            //圖片存放物理路徑
            var savePhysicalPath = HttpContext.Request.MapPath("~/qrcode/");

            var imgBack = Image.FromFile(savePhysicalPath + "ewm.jpg");//合成背景圖片
            var wechatQrcodeImg = GetWetchatAppletQRCodeImage(GetWechatAccessToken(),userId.ToString());//獲取小程序碼圖片
            var compositePictureUrl = CompositePicture(imgBack, wechatQrcodeImg, savePhysicalPath, 232, 719, 290, 290);

            return Json(new { code = 0, compositePictureUrl = compositePictureUrl });
        }

        /// <summary>
        /// 合成圖片
        /// </summary>
        /// <param name="backgroundImage">背景圖</param>
        /// <param name="qrCodeImg">二維碼圖片</param>
        /// <param name="savePhysicalPath">圖片存放物理路徑</param>
        /// <param name="xDeviation">繪製圖像X軸偏差</param>
        /// <param name="yDeviation">繪製圖像Y軸偏差</param>
        /// <param name="width">繪製圖像寬</param>
        /// <param name="height">繪製圖像高</param>
        /// <returns></returns>
        public string CompositePicture(Image backgroundImage, Image qrCodeImg, string savePhysicalPath, int xDeviation = 0, int yDeviation = 0, int width = 0, int height = 0)
        {
            Bitmap bitmap = new Bitmap(backgroundImage.Width, backgroundImage.Height);
            Graphics graphics = Graphics.FromImage(bitmap);//繪圖
            graphics.Clear(Color.White);
            SolidBrush surush = new SolidBrush(Color.White);
            graphics.DrawImage(backgroundImage, 0, 0, backgroundImage.Width, backgroundImage.Height);
            graphics.DrawImage(qrCodeImg, xDeviation, yDeviation, width, height);
            GC.Collect();//垃圾清理

            string compositePictureUrl = savePhysicalPath + Guid.NewGuid().ToString() + ".jpg";
            //合成圖片保存
            bitmap.Save(compositePictureUrl, System.Drawing.Imaging.ImageFormat.Jpeg);

            return compositePictureUrl;
        }

合成效果圖

DotNetGuide技術社區交流羣

  • DotNetGuide技術社區是一個面向.NET開發者的開源技術社區,旨在爲開發者們提供全面的C#/.NET/.NET Core相關學習資料、技術分享和諮詢、項目推薦、招聘資訊和解決問題的平臺。
  • 在這個社區中,開發者們可以分享自己的技術文章、項目經驗、遇到的疑難技術問題以及解決方案,並且還有機會結識志同道合的開發者。
  • 我們致力於構建一個積極向上、和諧友善的.NET技術交流平臺,爲廣大.NET開發者帶來更多的價值和成長機會。

歡迎加入DotNetGuide技術社區微信交流羣👪

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章