目录
官方开发文档
https://developers.weixin.qq.com/miniprogram/dev/api/qrcode.html
注意点:开发者切记,选用适合的接口开发自己的业务。
tip
:通过该接口,仅能生成已发布的小程序的二维码。tip
:可以在开发者工具预览时生成开发版的带参二维码。tip
:接口A加上接口C,总共生成的码数量限制为100,000,请谨慎调用。tip
: POST 参数需要转成 json 字符串,不支持 form 表单提交。tip
: auto_color line_color 参数仅对小程序码生效。
错误码:
- 45009:B接口调用分钟频率受限(目前5000次/分钟,会调整),如需大量小程序码,建议预生成。
- 45029:A接口和C接口生成码个数总和到达最大个数限制。
- 41030:B接口所传page页面不存在,或者小程序没有发布,请注意B接口没有path参数,传path参数虽然可以生成小程序码,但是只能跳主页面。
总结:
接口A: 适用于需要的码数量较少的业务场景
接口地址:
https://api.weixin.qq.com/wxa/getwxacode?access_token=ACCESS_TOKEN
接口B:适用于需要的码数量极多的业务场景
接口地址:
https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
接口C:适用于需要的码数量较少的业务场景。
接口地址:
https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN
接口A、C虽然能传递path,直接跳转到对应的页面。但由于不能大量的生成,当需要生成的二维码大于的100,000个时候,又想跳转到对应的页面。那么我们需要使用B接口。原因如下:
- 生成的二维码数量不限。
- B接口带有scene参数,前端能根据扫码得到scene参数进行逻辑的判断,从而跳转到对应的页面。
实例代码
B接口实例代码:
/**
* @Description: 获取二维码
* @param: @param
* m
* @param: @return
* @return: Return
*/
public Return getQr(HashMap<String, Object> m) {
HttpSession session = getSession();
String scene = m.get("scene").toString; // 自定义参数
String path = "pages/index/index";// 主页地址
if (!"".equals(Tools.toStr(m.get("page")))) { // 首页地址
path = Tools.toStr(m.get("page"));
}
int width = (Tools.toInt(m.get("width")) == 0) ? 430 : Tools.toInt(m.get("width")); // 二维码的宽度
Boolean auto_color = Tools.toBoolean(m.get("auto_color")); // 默认false,自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调
// 当auto_color为false才生效
JSONObject line_color = new JSONObject();
line_color.put("r", 0);
line_color.put("g", 0);
line_color.put("b", 0);
boolean is_hyaline = Tools.toBoolean(m.get("is_hyaline"));// 默认false,是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码
scene = "test";//测试
auto_color = true;// 测试
is_hyaline = false;// 测试
Map<String, Object> params = new HashMap<>();
params.put("scene", scene);
params.put("width", width);
params.put("path", path);
params.put("auto_color", auto_color);
params.put("line_color", line_color);
params.put("is_hyaline", is_hyaline);
String accessToken = Tools.toStr(session.getAttribute("wxapp_access_token"));//临时令牌,有效期2小时
if ("".equals(accessToken)) {
// 获取accessToken
accessToken = getAccessToken(session);
}
// 二维码接口url
String getwxacodeunlimit = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";
String url = getwxacodeunlimit + accessToken;
// 发起请求
HttpResponse response;
InputStream ins = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();//二进制输出
FileOutputStream fileOut = null;//图片文件输出
StringEntity entity;
try {
File targetFile = new File("/home/m/桌面/5.jpeg");// 存放位置
fileOut = new FileOutputStream(targetFile);
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(url); // 接口
httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
String body = JSON.toJSONString(params); // 必须是json模式的 post
entity = new StringEntity(body);
entity.setContentType("image/jpeg");
httpPost.setEntity(entity);
response = client.execute(httpPost);
ins = response.getEntity().getContent();
byte[] buffer = new byte[8192];
int bytesRead = 0;
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
fileOut.write(buffer, 0, bytesRead);
out.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
e.printStackTrace();
log.error("生成微信小程序二维码失败,失败原因:" + e.getMessage());
} finally {
if (ins != null) {
try {
ins.close();
} catch (IOException e) {
log.error("生成微信小程序二维码失败,失败原因:" + e.getMessage());
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
if (fileOut != null) {
try {
fileOut.close();
} catch (IOException e) {
log.error("生成微信小程序二维码失败,失败原因:" + e.getMessage());
}
}
}
System.out.println("生成完毕!");// 上传时删掉
return NULL;
}
/**
* 获取access_token //
* https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
* @param url
* @param grantType
* @param appid
* @param secret
* @return
*/
public static String getAccessToken(HttpSession session) {
// 清除旧accessToken
if (session.getAttribute("wxapp_access_token") != null) {
session.removeAttribute("wxapp_access_token");
}
String url = SystemGlobals.getValue(WxAppConstants.WXAPP_ACCESS_TOKEN_URL);
String appid = SystemGlobals.getValue(WxAppConstants.WXAPP_APPID);// appid
String wxAppKey = SystemGlobals.getValue(WxAppConstants.WXAPP_APPKEY);// secret
String grantType = SystemGlobals.getValue(WxAppConstants.WXAPP_GRANTTYPE_ACCESS_TOKEN);// grant_type = client_credential
log.info(url+" appid:"+appid+" appkey:"+wxAppKey);
String access_token = "";
try {
String jsonStr = HttpClientUtil.get(MessageFormat.format(url, grantType, appid, wxAppKey));
com.alibaba.fastjson.JSONObject json = com.alibaba.fastjson.JSONObject.parseObject(jsonStr);
access_token = Tools.toStr(json.get("access_token"));
} catch (Exception e) {
log.error("微信小程序获取access_Token失败: 错误原因:"+e.getMessage());
}
// 添加新的accessToken
session.setAttribute("wxapp_access_token", access_token);
return access_token;
}
上面代码能生成带参的二维码。
但由于我们有时候需要更换二维码中间的logo,由于微信小程序默认生成的是我们自己微信小程序的头像。所以需要动点手脚,更换中间logo。
- 将需要更换的logo按比例缩小成透明底圆形logo,源logo图片最好是正方形,这样压缩出来的图片就不会变成椭圆形。
- 准备一张已生成带有参数的二维码参数,也是正方形的图片。
- 将两张图片合并。
更换中间logo实例代码:
实例代码,已抽成工具类
package com.jobcn.wxapp.util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.imageio.ImageIO;
/**
* @Description:微信小程序二维码专用工具类
* @author: mozhipeng
* @date: 2018年7月24日 上午11:57:24
*/
public class WxAppImageUtil {
static String url = "https://avatar.csdn.net/3/1/7/1_qq_27292113.jpg?1488183229974";
public static void main(String[] args) {
try {
// https://avatar.csdn.net/3/1/7/1_qq_27292113.jpg?1488183229974
// 是头像地址
// 获取图片的流
BufferedImage url = getUrlByBufferedImage("http://192.168.61.125/Person/avatar/qN10");
// Image src = ImageIO.read(new
// File("C:/Users/Administrator/Desktop/Imag.png"));
// BufferedImage url = (BufferedImage) src;
// 处理图片将其压缩成正方形的小图
BufferedImage convertImage = scaleByPercentage(url, 100, 100);
// 裁剪成圆形 (传入的图像必须是正方形的 才会 圆形 如果是长方形的比例则会变成椭圆的)
convertImage = convertCircular(url);
// 生成的图片位置
String imagePath = "/home/mozhipeng/桌面/6.jpeg";
ImageIO.write(convertImage, imagePath.substring(imagePath.lastIndexOf(".") + 1), new File(imagePath));
System.out.println("ok");
/// -------------------------------------
WxAppImageUtil tt = new WxAppImageUtil();
BufferedImage d = tt.loadImageLocal("/home/mozhipeng/桌面/5.jpeg");
BufferedImage b = tt.loadImageLocal("/home/mozhipeng/桌面/6.jpeg");
// 往图片上写文件
// tt.writeImageLocal("E:\\ploanshare\\2\\22.jpg", tt.modifyImage(d, "000000",
// 90, 90));
tt.writeImageLocal("/home/mozhipeng/桌面/7.jpeg", tt.modifyImagetogeter(b, d));
// 将多张图片合在一起
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 合并图片
* @param: @param srcImage1 合并图片1.作为首层
* @param: @param srcImage2 合并图片2,作为底层
* @param: @param destImage 合并后存放的位置
* @return: void
*/
public static void mergePictures(String srcImage1,String srcImage2,String destImage) {
WxAppImageUtil tt = new WxAppImageUtil();
BufferedImage d = tt.loadImageLocal(srcImage1);
BufferedImage b = tt.loadImageLocal(srcImage2);
// 往图片上写文件
// tt.writeImageLocal("E:\\ploanshare\\2\\22.jpg", tt.modifyImage(d, "000000",
// 90, 90));
tt.writeImageLocal(destImage, tt.modifyImagetogeter(b, d));
// 将多张图片合在一起
System.out.println("success");
}
/**
* @Description: 讲图片按比例缩小且裁剪成圆形(图片来源:网络)
* @param: @param
* srcUrl 图片源地址
* @param: @param
* destUrl 图片存放地址
* @param: @param
* type 图片类型(png,jpeg),默认jpeg
* @param: @throws
* Exception
* @return: void
*/
public static void cutPic2Circular(String srcUrl, String destUrl, String type) throws Exception {
BufferedImage url = getUrlByBufferedImage(srcUrl);
// Image src = ImageIO.read(new
// File("C:/Users/Administrator/Desktop/Imag.png"));
// BufferedImage url = (BufferedImage) src;
// 处理图片将其压缩成正方形的小图
BufferedImage convertImage = scaleByPercentage(url, 100, 100);
// 裁剪成圆形 (传入的图像必须是正方形的 才会 圆形 如果是长方形的比例则会变成椭圆的)
convertImage = convertCircular(url);
// 生成的图片位置
String imagePath = destUrl + "." + type;
ImageIO.write(convertImage, imagePath.substring(imagePath.lastIndexOf(".") + 1), new File(imagePath));
System.out.println("ok");
}
/**
* @Description: 讲图片按比例缩小且裁剪成圆形(图片来源:本地)
* @param: @param
* srcUrl 图片源地址
* @param: @param
* destUrl 图片存放地址
* @param: @param
* type 图片类型(png,jpeg),默认jpeg
* @param: @throws
* Exception
* @return: void
*/
public static void cutPic2CircularLocal(String srcUrl, String destUrl, String type) throws Exception {
// BufferedImage url = getUrlByBufferedImage(srcUrl);
Image src = ImageIO.read(new File(srcUrl));
BufferedImage url = (BufferedImage) src;
// 处理图片将其压缩成正方形的小图
BufferedImage convertImage = scaleByPercentage(url, 100, 100);
// 裁剪成圆形 (传入的图像必须是正方形的 才会 圆形 如果是长方形的比例则会变成椭圆的)
convertImage = convertCircular(url);
// 生成的图片位置
String imagePath = destUrl + "." + type;
ImageIO.write(convertImage, imagePath.substring(imagePath.lastIndexOf(".") + 1), new File(imagePath));
System.out.println("ok");
}
/**
* 缩小Image,此方法返回源图像按给定宽度、高度限制下缩放后的图像
*
* @param inputImage
* @param maxWidth
* :压缩后宽度
* @param maxHeight
* :压缩后高度
* @throws java.io.IOException
* return
*/
public static BufferedImage scaleByPercentage(BufferedImage inputImage, int newWidth, int newHeight)
throws Exception {
// 获取原始图像透明度类型
int type = inputImage.getColorModel().getTransparency();
int width = inputImage.getWidth();
int height = inputImage.getHeight();
// 开启抗锯齿
RenderingHints renderingHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// 使用高质量压缩
renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
BufferedImage img = new BufferedImage(newWidth, newHeight, type);
Graphics2D graphics2d = img.createGraphics();
graphics2d.setRenderingHints(renderingHints);
graphics2d.drawImage(inputImage, 0, 0, newWidth, newHeight, 0, 0, width, height, null);
graphics2d.dispose();
return img;
}
/**
* 通过网络获取图片
*
* @param url
* @return
*/
public static BufferedImage getUrlByBufferedImage(String url) {
try {
URL urlObj = new URL(url);
HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();
// 连接超时
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setConnectTimeout(25000);
// 读取超时 --服务器响应比较慢,增大时间
conn.setReadTimeout(25000);
conn.setRequestMethod("GET");
conn.addRequestProperty("Accept-Language", "zh-cn");
conn.addRequestProperty("Content-type", "image/jpeg");
conn.addRequestProperty("User-Agent",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)");
conn.connect();
BufferedImage bufImg = ImageIO.read(conn.getInputStream());
conn.disconnect();
return bufImg;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 传入的图像必须是正方形的 才会 圆形 如果是长方形的比例则会变成椭圆的
*
* @param url
* 用户头像地址
* @return
* @throws IOException
*/
public static BufferedImage convertCircular(BufferedImage bi1) throws IOException {
// BufferedImage bi1 = ImageIO.read(new File(url));
// 这种是黑色底的
// BufferedImage bi2 = new BufferedImage(bi1.getWidth(), bi1.getHeight(),
// BufferedImage.TYPE_INT_RGB);
// 透明底的图片
BufferedImage bi2 = new BufferedImage(bi1.getWidth(), bi1.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, bi1.getWidth(), bi1.getHeight());
Graphics2D g2 = bi2.createGraphics();
g2.setClip(shape);
// 使用 setRenderingHint 设置抗锯齿
g2.drawImage(bi1, 0, 0, null);
// 设置颜色
g2.setBackground(Color.green);
g2.dispose();
return bi2;
}
// --------------------------------------------------------------------------------------
/**
* 把两张图片合并
*
* @author lizhiyong
* @version $Id: Pic.java, v 0.1 2015-6-3 下午3:21:23 1111 Exp $
*/
private Font font = new Font("宋体", Font.PLAIN, 12); // 添加字体的属性设置
private Graphics2D g = null;
private int fontsize = 0;
private int x = 0;
private int y = 0;
/**
* 导入本地图片到缓冲区
*/
public BufferedImage loadImageLocal(String imgName) {
try {
return ImageIO.read(new File(imgName));
} catch (IOException e) {
System.out.println(e.getMessage());
}
return null;
}
/**
* 导入网络图片到缓冲区
*/
public BufferedImage loadImageUrl(String imgName) {
try {
URL url = new URL(imgName);
return ImageIO.read(url);
} catch (IOException e) {
System.out.println(e.getMessage());
}
return null;
}
/**
* 生成新图片到本地
*/
public void writeImageLocal(String newImage, BufferedImage img) {
if (newImage != null && img != null) {
try {
File outputfile = new File(newImage);
ImageIO.write(img, "jpg", outputfile);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
/**
* 设定文字的字体等
*/
public void setFont(String fontStyle, int fontSize) {
this.fontsize = fontSize;
this.font = new Font(fontStyle, Font.PLAIN, fontSize);
}
/**
* 修改图片,返回修改后的图片缓冲区(只输出一行文本)
*/
public BufferedImage modifyImage(BufferedImage img, Object content, int x, int y) {
try {
int w = img.getWidth();
int h = img.getHeight();
g = img.createGraphics();
g.setBackground(Color.WHITE);
g.setColor(Color.orange);// 设置字体颜色
if (this.font != null)
g.setFont(this.font);
// 验证输出位置的纵坐标和横坐标
if (x >= h || y >= w) {
this.x = h - this.fontsize + 2;
this.y = w;
} else {
this.x = x;
this.y = y;
}
if (content != null) {
g.drawString(content.toString(), this.x, this.y);
}
g.dispose();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return img;
}
/**
* 修改图片,返回修改后的图片缓冲区(输出多个文本段) xory:true表示将内容在一行中输出;false表示将内容多行输出
*/
public BufferedImage modifyImage(BufferedImage img, Object[] contentArr, int x, int y, boolean xory) {
try {
int w = img.getWidth();
int h = img.getHeight();
g = img.createGraphics();
g.setBackground(Color.WHITE);
g.setColor(Color.RED);
if (this.font != null)
g.setFont(this.font);
// 验证输出位置的纵坐标和横坐标
if (x >= h || y >= w) {
this.x = h - this.fontsize + 2;
this.y = w;
} else {
this.x = x;
this.y = y;
}
if (contentArr != null) {
int arrlen = contentArr.length;
if (xory) {
for (int i = 0; i < arrlen; i++) {
g.drawString(contentArr[i].toString(), this.x, this.y);
this.x += contentArr[i].toString().length() * this.fontsize / 2 + 5;// 重新计算文本输出位置
}
} else {
for (int i = 0; i < arrlen; i++) {
g.drawString(contentArr[i].toString(), this.x, this.y);
this.y += this.fontsize + 2;// 重新计算文本输出位置
}
}
}
g.dispose();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return img;
}
/**
* 修改图片,返回修改后的图片缓冲区(只输出一行文本)
*
* 时间:2007-10-8
*
* @param img
* @return
*/
public BufferedImage modifyImageYe(BufferedImage img) {
try {
int w = img.getWidth();
int h = img.getHeight();
g = img.createGraphics();
g.setBackground(Color.WHITE);
g.setColor(Color.blue);// 设置字体颜色
if (this.font != null)
g.setFont(this.font);
g.drawString("www.hi.baidu.com?xia_mingjian", w - 85, h - 5);
g.dispose();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return img;
}
public BufferedImage modifyImagetogeter(BufferedImage b, BufferedImage d) {
try {
int w = b.getWidth();
int h = b.getHeight();
g = d.createGraphics();
g.drawImage(b, 150, 150, w, h, null);
g.dispose();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return d;
}
}