前言
对于小程序大家可能都非常熟悉了,随着小程序的不断普及越来越多的公司都开始推广使用起来了。今天接到一个需求就是生成小程序码,并且与运营给的推广图片合并在一起做成一张漂亮美观的推广二维码,扫码这种二维码就可以进入小程序。为了节省服务器内存资源,我想的就是成功调用通微信生成小程序码的接口后直接把微信返回过来的图片二进制内容(返回的图片 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接口调用凭证。一般来说access_token的有效时长为2小时,为了不频繁调用该接口我们可以通过缓存的方法把调用凭证存起来并设置合理的过期时间(redis,cookie,memorycache都是非常不错的选择)。
请求地址:
- GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
请求参数:
返回值(JSON 数据包):
请求代码:
/// <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; } }
小程序码获取
请求地址:
请求参数:
请求成功返回值:
返回的图片 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; }