zoukankan      html  css  js  c++  java
  • .NET生成小程序码,并合自定义背景图生成推广小程序二维码

    前言:

      对于小程序大家可能都非常熟悉了,随着小程序的不断普及越来越多的公司都开始推广使用起来了。今天接到一个需求就是生成小程序码,并且与运营给的推广图片合并在一起做成一张漂亮美观的推广二维码,扫码这种二维码就可以进入小程序。为了节省服务器内存资源,我想的就是成功调用通微信生成小程序码的接口后直接把微信返回过来的图片二进制内容(返回的图片 Buffer)转化为二进制byte[]文件流,然后再转成Image这样就不需要在保存到本地直接读取本地的背景图片通过GDI+(Graphics)绘制图片。废话不多说直接上码,各位同学假如有什么小程序的开发问题都欢迎评论区,或者qq私聊我有时间都可以一起学习探索。

    选择小程序码生成方式:

    首先微信小程序官方文档提供了三种生成小程序码的方法,如下所示(本文采用的是第三种,需要的码数量极多的业务场景):

    文档详情地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.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

    请求参数:

    属性类型默认值必填说明
    grant_type string   填写 client_credential
    appid string   小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态)
    secret string   小程序唯一凭证密钥,即 AppSecret,获取方式同 appid

    返回值(JSON 数据包):

    属性类型说明
    access_token string 获取到的凭证
    expires_in number 凭证有效时间,单位:秒。目前是7200秒之内的值。
    errcode number 错误码
    errmsg string 错误信息

    请求代码:

    /// <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   接口调用凭证
    scene string   最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
    page string 主页 必须是已经发布的小程序存在的页面(否则报错),例如 pages/index/index, 根路径前不要填加 /,不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面
    width number 430 二维码的宽度,单位 px,最小 280px,最大 1280px
    auto_color boolean false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调,默认 false
    line_color Object {"r":0,"g":0,"b":0} auto_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} 十进制表示
    is_hyaline boolean false 是否需要透明底色,为 true 时,生成透明底色的小程序

    请求成功返回值:

    返回的图片 Buffer(如果调用成功,会直接返回图片二进制内容(图片文件流),如果请求失败,会返回 JSON 格式的数据。)

    请求异常返回值:

    属性类型说明
    errcode number 错误码
    errmsg string 错误信息

    请求代码:

    注意:这个与前面获取授权凭证的网络请求不同的是因为要接收请求返回过来的图片二进制内容(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;
            }

    合成效果图:

    作者:追逐时光者

    作者简介:一个热爱编程,善于分享,喜欢学习、探索、尝试新事物,新技术的程序猿。

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得时光这篇文章有帮助的话,可以点一下右下角的【♥推荐♥】,希望能够持续的为大家带来好的技术文章,文中可能存在描述不正确或错误的地方,欢迎指正、补充,不胜感激 !

  • 相关阅读:
    Spring框架之环绕增强
    Spring框架之最终增强
    Spring框架使用注解实现IOC的配置
    [算法] 带有条件的全排列 [dfs + set]
    [算法] 字符串的全排列 [dfs 递归神技]
    [面试] 十七道海量数据处理面试题与Bitmap详解 [未细看]
    [算法] 输出 字符串的全部子组合 [dfs 递归神技]
    [linux] 进程间通讯的几种方式
    [面试] 在数组查找这样的数,它大于等于左侧所有数,小于等于右侧所有数
    [算法] 彻头彻尾理解二分检索 6 种变换
  • 原文地址:https://www.cnblogs.com/Can-daydayup/p/14753662.html
Copyright © 2011-2022 走看看