微信小程序开发二维码生成工具类
- 前言
- 业务背景
- 设计思路
- 具体实现
- 接下来我们进行工具的改造
前言
或许这是你搜寻的第n篇文章来解决你项目中的问题,虽然我不能去替你完全适配你的业务需求,但是我可以给你一个解决问题的思路!或许 这是你第一次认识我这个 码农 但是欢迎你关注我,后续我会持续更新本博客,并耐心为各位进行回复,你们的支持就是我写下去的动力!话不多说让我们来讲一下今天的业务背景以及我的思路,如有不对欢迎批评指正。
业务背景
在当下轻应用盛行的时代,微信小程序开发已然在软件开发中占据了举足轻重的地位,不知从何时起,我们去面试的时候虽然投递的简历是后端开发工程师或Java开发工程师,但我们总会碰到面试官问的一句会前端开发么?了解小程序开发么?
这篇博客是我在实际项目做后端开发技术支持时,小程序开发人员抛给我的一个问题,现在市面上的二维码生成技术无非前端使用QRcode来实现,CSDN中的前端二维码生成博客或者小程序二维码生成博客十篇有八篇时使用的这项技术,实际在微信小程序开发中,微信小程序的服务端api给出了一个生成小程序二维码的api。双方生成的区别之处,使用QRcode生成的二维码第一点我不太能接受的就是像业务经常说的为什么别的开发可以做出的效果你做不出来?为什么别人的小程序分享出来的二维码是一个圆形的中间带有log且扫描后直接可以到小程序内的,而你的是一个方方正正的样子呢虽然看的过去但是体验不怎么棒的呢?
设计思路
1、刚开始,我们使用前端即小程序端来生成相应的宣传海报,附带二维码,开始时前端选用QRcode进行实现,虽然可以生成但是效果并不怎么样,且前端需要使用到小程序的appid以及secret或者额外做一次交互,由后端使用http请求携带appid及secret获取token然后返回给前端,前端再进行二维码的获取,但坏处是无法将二维码落地存储,因使用的是微信的api且微信的api表示该二维码无时间限制。最终我们将换取token以及拿到二维码两次调用全部交由后端来实现,方便二维码进行保存。
2、使用到的api
获取Token:auth.getAccessToken点击api可以直达微信官方的文档。
获取二维码:wxacode.getUnlimited点击api可以直达微信官方文档。
具体实现
1、我们知道在我们的项目中经常会用到http请求get或者post居多,很多时候我们会在项目中创建相应的http的get或者post工具类本次我们实现此工具同样会用到我们写http请求的东西。
首先我们看下我们常见的http的工具类
package com.example.test.ExampleProject.util;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;/*** http获取文件并下载*/
public class HttpRequestUtils {/*** 向指定URL发送GET方法的请求** @param url* 发送请求的URL* @param param* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。* @return result 所代表远程资源的响应结果*/public static String sendGet(String url, String param) {//接受返回值String result = "";//读取相应的BufferedReaderBufferedReader in = null;try {//拼接get请求的地址及参数如下//localhost:8080/项目名/拦截地址.json?name=1&file=2String urlNameString = url + "?" + param;//将拼接的链接数据放入URL里用于后面打开链接及发送数据URL realUrl = new URL(urlNameString);// 打开和URL之间的连接URLConnection connection = realUrl.openConnection();// 设置通用的请求属性connection.setRequestProperty("accept", "*/*");connection.setRequestProperty("connection", "Keep-Alive");connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; MAC OS 5.1;SV1)");// 建立实际的连接connection.connect();// 获取所有响应头字段若需要则进行处理不需要可删除Map<String, List<String>> map = connection.getHeaderFields();// 遍历所有的响应头字段for (String key : map.keySet()) {System.out.println(key + "--->" + map.get(key));}// 定义 BufferedReader输入流来读取URL的响应in = new BufferedReader(new InputStreamReader(connection.getInputStream()));String line;while ((line = in.readLine()) != null) {result += line;}} catch (Exception e) {System.out.println("发送GET请求出现异常!" + e);e.printStackTrace();}// 使用finally块来关闭输入流finally {try {if (in != null) {in.close();}} catch (Exception e2) {e2.printStackTrace();}}return result;}/*** 向指定 URL 发送POST方法的请求** @param url* 发送请求的 URL* @param param* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。* @return 所代表远程资源的响应结果*/public static String sendPost(String url, Map param) {PrintWriter out = null;BufferedReader in = null;String result = "";try {URL realUrl = new URL(url);// 打开和URL之间的连接URLConnection conn = realUrl.openConnection();// 设置通用的请求属性conn.setRequestProperty("accept", "*/*");conn.setRequestProperty("connection", "Keep-Alive");conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");// 发送POST请求必须设置如下两行conn.setDoOutput(true);conn.setDoInput(true);// 获取URLConnection对象对应的输出流out = new PrintWriter(conn.getOutputStream());// 发送请求参数out.print(param);// flush输出流的缓冲out.flush();// 定义BufferedReader输入流来读取URL的响应in = new BufferedReader(new InputStreamReader(conn.getInputStream()));String line;while ((line = in.readLine()) != null) {result += line;}} catch (Exception e) {System.out.println("发送 POST 请求出现异常!"+e);e.printStackTrace();}//使用finally块来关闭输出流、输入流finally{try{if(out!=null){out.close();}if(in!=null){in.close();}}catch(IOException ex){ex.printStackTrace();}}return result;}
}
上面可以看到我们使用的基本的一个httpget和post主要使用的是Java的IO包中的输入输出流和Java的net包中的URL和URLConnection并不复杂的一个东西,如果你看过了我上面的api链接可以看到微信的生成二维码的部分wxacode.getUnlimited是返回的Buffer我们暂且可以认为他是一个流。那我们后续处理就简单了起来首先我们需要从微信获取token也就是调用get请求一个地址拿到其返回值,这一步我不需要过多解释若您不懂可以现使用postMan工具请求一下看下返回值,方便你后续进行理解。
接下来我们进行工具的改造
1、首先:我们需要了解自己的需求,两种选择,第一我们将生成的二维码落地,第二我们直接返回二维码并不在服务端进行保存。
2、在这我们先讲第二种的情况也就是直接返回二维码并不在服务端保存,在这呢微信给我们埋了一个坑,我们在调用微信的wxacode.getUnlimited时要注意链接地址并不单纯,我们需要将第一步get获取的access_token放在URL上,然后将scene、page、width等这些参数放入Map中在postMan中将width这些参数放入body中(postMan如下图)
好我们这里的一个小坑已经讲完了那我们是不是直接放入上面工具类中直接请求返回就ok了?答案是并不行的,原因是我们上面的工具类返回的是一个String类型,而微信给我们的文档是返回一个Buffer。那我们怎么去改呢?这时候第二个坑就来了。注意:若我们直接返回图片不带任何东西只返回图片流的话我们需要知道一个东西就是HttpServletResponse类
我们既然要直接返回图片的话那我们一定是直接通过HttpServletResponse的流输出去的,你的Controller返回直接为void即可。
那我们此时进行改造即可首先进行Controller改造代码如下仅供参考。
package com.webappdemo.controller;import com.haishi.webappdemo.utils.HttpRequestUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;@RestController
public class TestController {@RequestMapping(value = "/getQRcode.json", method = RequestMethod.GET)public void getQRcode(@RequestParam("url") String url, HttpServletResponse response){Map<String, String> param = new HashMap<>();HttpRequestUtils.sendPost(url, param, response);}}
我们这直接看可能会有点懵是因为刚刚上面的工具类是接收的两个参数怎么你在Controller内就三个了?这个是因为刚刚我说过了直接通过HttpServletResponse进行图片的流的输出所以我这边工具类进行了更改。注意我这边通过Controller直接调用的工具类实际应用中我们应是调用应用层然后到服务层由服务层去直接交互工具类具体使用方法按照各位项目来!
下面我们开始写工具类改造的思路:
- 首先:通过注释我们可以看到我们将传入的url放入了Url类中然后使用URLConnection打开链接,get与post方式不同之处是post使用了new PrintWriter(conn.getOutputStream());获取了输出流,然后通过print方法发送参数,刷新输出流的缓存最终发送过去数据。
- 其次:我们如何解决图片的问题?我们可以看到现在代码中使用BufferedReader接了一下链接上的输入流也就是URL的响应。那我们就来改造这部分话不多说先上代码
/*** 向指定 URL 发送POST方法的请求** @param url* 发送请求的 URL* @param param* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。* @return 所代表远程资源的响应结果*/public static void sendPost(String url, Map param, HttpServletResponse response) {PrintWriter out = null;InputStream in = null;try {URL realUrl = new URL(url);// 打开和URL之间的连接URLConnection conn = realUrl.openConnection();// 设置通用的请求属性conn.setRequestProperty("accept", "*/*");conn.setRequestProperty("connection", "Keep-Alive");conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");// 发送POST请求必须设置如下两行conn.setDoOutput(true);conn.setDoInput(true);// 获取URLConnection对象对应的输出流out = new PrintWriter(conn.getOutputStream());// 发送请求参数out.print(param);// flush输出流的缓冲out.flush();// 定义BufferedReader输入流来读取URL的响应in = conn.getInputStream();OutputStream outputStream = response.getOutputStream();int len = 0;byte[] bytes = new byte[1024];while ((len = in.read(bytes)) != -1) {outputStream.write(bytes, 0, len);}outputStream.flush();outputStream.close();} catch (Exception e) {System.out.println("发送 POST 请求出现异常!"+e);e.printStackTrace();}//使用finally块来关闭输出流、输入流finally{try{if(out!=null){out.close();}if(in!=null){in.close();}}catch(IOException ex){ex.printStackTrace();}}}
从上方代码中可以看到我们好像并没有返回任何东西,那么图片怎么能回去呢?实际是通过了HttpServletResponse获取了OutputStream然后使用OutputStream内的write方法直接返回的。若不太理解可联想上方中out = new PrintWriter(conn.getOutputStream());这行代码结合理解,若不太理解可暂时认为他俩原理相同,即在开启的http请求中可以通过获取链接中的输出流将数据或值放入,然后刷新流进行输出这样对方就会接收到然后我们关闭流即本次连接结束。
上方我们主要改造了两处:
第一处就是我们没有使用之前的BufferReader了改为了Input Stream 第二呢肯定就是我们的Output Stream的改造,实际上我们可选择的方式很多,这仅仅是demo欢迎各位指正错误之处,也欢迎各位提出更好的方案!感谢!