草庐IT

Java生成微信小程序二维码,5种实现方式,一个比一个简单

天罡gg 2023-04-13 原文

文章目录


前言

先介绍一下项目场景,主要是通过微信小程序二维码裂变分享,每个账号有专属邀请二维码,分享出去,有新人扫码入驻,就可以得到现金奖励或红包奖励。当然,产品设计会更丰富,不止有裂变模式,还有渠道推广模式,还有各种奖励规则,但核心实现都是生成二维码。对于如何生成微信小程序二维码,本文一共列举了5种实现方式,其中第1、2种是网上常见的方式,第3、4、5种封装的更为优雅,文末可打包下载开箱即用的全套源码,我更期待老铁的文末 投票 :哪种方式你更喜欢?


先看官网

项目主要用的是微信官方提供的服务端能力,考虑到涉及secrettoken等安全问题,所以从架构上设计的调用链路为前端->后端API->微信API。裂变的场景决定要选择获取不限制的小程序码接口,永久有效,数量暂无限制,接口英文名:getUnlimitedQRCode

官方地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html


一、JDK自带的URLConnection方式

在网上常见的方式,这是从 JDK1.1 开始就自带的Http请求方式,核心代码如下:

public byte[] getWechatQrcodeByHttpURL(String url, Map<String, Object> body) {
    HttpURLConnection httpURLConnection = null;
    try {
        httpURLConnection = (HttpURLConnection) new URL(url).openConnection();
        httpURLConnection.setRequestMethod("POST");
        // 发送POST请求必须设置如下两行
        httpURLConnection.setDoOutput(true);
        httpURLConnection.setDoInput(true);
        // 获取URLConnection对象对应的输出流
        PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());
        // 发送请求参数
        printWriter.write(JSONObject.toJSONString(body));
        // flush输出流的缓冲
        printWriter.flush();
        //开始获取数据
        try (InputStream inputStream = httpURLConnection.getInputStream();
             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = inputStream.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            return out.toByteArray();
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (httpURLConnection != null) {
            httpURLConnection.disconnect();
        }
    }
    return null;
}

JDK自带的方式,功能虽然可以实现,但代码看上去确实跟不上时代发展了!


二、Apache的HttpClient方式

HttpClient 相比于 JDK 自带的 URLConnection方式,代码做了封装,可读性和简洁度都显著提升!核心代码如下:

public byte[] getWechatQrcodeByHttpClient(String url, Map<String, Object> body) {
    CloseableHttpClient httpClient = HttpClientBuilder.create().build();
    HttpPost httpPost = new HttpPost(url);
    try {
        StringEntity entity = new StringEntity(JSONObject.toJSONString(body));
        entity.setContentType("image/png");
        httpPost.setEntity(entity);
        HttpResponse response = httpClient.execute(httpPost);
        try (InputStream inputStream = response.getEntity().getContent();
             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = inputStream.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            return out.toByteArray();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

Maven依赖:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.12</version>
</dependency>

三、okhttp3方式

okhttp3是Square公司开发,用于替代 HttpUrlConnection和Apache HttpClient方式,优雅的 API 设计,且链式调用,让你顺滑到底!

核心代码如下:

public byte[] getWechatQrcodeByOkhttp3(String url, Map<String, Object> body) {
    OkHttpClient client = new OkHttpClient().newBuilder().build();
    okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/json");
    RequestBody requestBody = RequestBody.create(mediaType, JSONObject.toJSONString(body));
    Request request = new Request.Builder().url(url).method("POST", requestBody).build();
    try {
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().bytes();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Maven依赖:

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.14.2</version>
</dependency>

四、Unirest方式

okhttp3方式其实不管是性能还是代码的链式调用,都已经非常优秀。但作为底层基于Apache HttpClient方式unirest-java,提供了更为便捷、链式调用、功能强大的API用于http请求!核心代码如下(一行代码搞定,屌爆了!!!):

public byte[] getWechatQrcodeByUnirest(String url, Map<String, Object> body) {
    return Unirest.post(url).body(JSONObject.toJSONString(body)).asBytes().getBody();
}

Maven依赖:

<dependency>
    <groupId>com.konghq</groupId>
    <artifactId>unirest-java</artifactId>
    <version>3.11.11</version>
</dependency>

五、RestTemplate方式

我之前写过一篇博文对RestTemplate.exchange结合案例进行了详细的介绍:RestTemplate.exchange各种用法(包括泛型等 --全),推荐阅读!

这也是我们项目实际使用的方式,使用Spring框架开发,还是强烈推荐使用Spring的RestTemplate,它是对于其它方式的高级封装,内部可以配置ClientHttpRequestFactory来指定底层请求方式:

  • 指定为SimpleClientHttpRequestFactory,则内部使用JDK自带的HttpURLConnection方式
  • 指定为HttpComponentsClientHttpRequestFactory,则内部使用Apache HttpClient方式
  • 指定为OkHttp3ClientHttpRequestFactory,则内部使用okhttp3方式

核心代码也是一行:

public byte[] getWechatQrcodeByRestTemplate(String url, Map<String, Object> body) {
    return restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(body, null), byte[].class).getBody();
}

另外,RestTemplate在构造函数中默认增加了ByteArrayHttpMessageConverter,可以读取和写入字节数组的HttpMessageConverter的实现,默认情况下,此转换器支持所有媒体类型(media types)。


其它细节

getAccessToken

这是接口调用凭证,用restTemplate调用也是非常简洁,核心代码如下:

private String wechatQrcodeUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";
private String appid = "替换成你的appid";
private String s = "替换成你的secret";

public String getAccessToken() {
    String url = String.format(wechatAccessTokenUrl, appid, s);
    Map<String, String> responseBody = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<Map<String, String>>() {}).getBody();
    if (responseBody == null || responseBody.get("errcode") != null) {
        // 获取失败
        System.out.println("getAccessToken失败:" + (responseBody == null ? "null" : responseBody.toString()));
        return null;
    }
    return responseBody.get("access_token");
}

对于access_token,建议保存到redis,2小时过期后再获取

构建参数map

body 就是参数Map,需要根据实际情况自行设定:

Map<String,Object> body = new HashMap<>();
// 场景码,与前端约定,最终是需要前端解析
body.put("scene", scene);
// 正式版为 "release",体验版为 "trial",开发版为 "develop"。默认是正式版。
body.put("env_version", envVersion);
// 透明,根据你的场景自行设置body参数
body.put("is_hyaline", true);

byte[]数组

为什么要先得到byte[]数组?,因为我们需要先判断返回结果是否包含errcode,如果不包含,才是图片Buffer!

如果不包含errorCode,那么byte[]数组不管是保存本地,还是转成Base64,或者上传到OSS,都非常湿滑了!

  • 保存本地
private void saveQrCodeToLocal(byte[] bytes) {
    try {
        InputStream inputStream = new ByteArrayInputStream(bytes);
        FileOutputStream out = new FileOutputStream("d:\\test.png");
        byte[] buffer = new byte[8192];
        int bytesRead = 0;
        while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
        out.flush();
        inputStream.close();
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • Base64
Base64.getEncoder().encodeToString(bytes);
  • 上传OSS
    这是我们项目使用的, 底层做了封装,直接传byte[]即可,非常方便!

源码下载

源码都是经过实测的,可以直接使用!

下载地址:https://download.csdn.net/download/scm_2008/87398320

❤️ 博客主页:https://blog.csdn.net/scm_2008
❤️ 欢迎点赞👍 收藏留言✏️ 如有错误敬请指正!
❤️ 本文由 天罡gg 原创,首发于 CSDN博客🙉
❤️ 停下休息的时候不要忘了别人还在奔跑,希望大家抓紧时间学习,全力奔赴更美好的生活


有关Java生成微信小程序二维码,5种实现方式,一个比一个简单的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  7. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  8. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  10. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

随机推荐