需求
在系统中生成一个二维码,用户保存下来并分享出去,其他人扫描之后跳到我们的一个活动详情页,查看此活动的内容。
需求分析
从以上的需求中,可以提炼出以下几点:
当用户点击生成二维码的时候,我们要拿到用户生成的二维码是关于哪个活动的;
请求来到后台,拿到活动ID,作为我们活动详情页的参数,生成一个url,作为扫描之后的跳转地址,生成二维码;
把生成的二维码,返回给前端,展示给用户;
用户保存二维码,即下载下来。
以上就是全部业务流程,二维码的内容,可以是一段明文,也可以是一个http或https链接,当扫描时会自动访问这个链接。对于大部分的场景,不能说大部分,应该说99%以上的场景都是给二维码的内容设置为一个链接,扫描之后跳到对应的页面再去执行业务
我们重点分析二维码的生成和用户下载二维码。
二维码如何返回给前端?也许很多人会想到,把生成的二维码存到服务器上,再把图片路径返回不就行了?这样当然可以,但是却多了很多不必要的操作:
对于二维码,我们只需要返回给用户即可,并不需要存到我们的服务器上,这没有任何意义,还占用磁盘空间;
我们可以直接将生成的二维码图片,以IO流的方式,通过response响应体直接返回给请求方。第一,不需要落到我们的磁盘,一切操作都在内存中完成,效率比较高;第二,所有生成二维码的请求,都可以访问这里,前端直接拿img标签的src就能访问,你在浏览器直接输入这个路径也能得到一张图片,减少了很多交互和逻辑处理。
再说第四步,我们直接在前端实现,因为前端有一种机制可以将某个img标签的图片下载下来,不需要浪费我们的服务器。
第一步 导入依赖
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.0.0</version>
</dependency>
第二步 编写工具类
package com.bqlg.wecom.app.itservice.feign;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 生成二维码的工具类
*/
public class QRCodeUtils {
/**
* 生成二维码
*
* @param content 二维码的内容
* @return BitMatrix对象
*/
public static BitMatrix createCode(String content) {
//二维码的宽高
int width = 200;
int height = 200;
//其他参数,如字符集编码
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
//容错级别为H
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//白边的宽度,可取0~4
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = null;
try {
//生成矩阵,因为我的业务场景传来的是编码之后的URL,所以先解码
bitMatrix = new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, width, height, hints);
//bitMatrix = deleteWhite(bitMatrix);
} catch (WriterException e) {
e.printStackTrace();
}
return bitMatrix;
}
/**
* 删除生成的二维码周围的白边,根据审美决定是否删除
* @param matrix BitMatrix对象
* @return BitMatrix对象
* */
private static BitMatrix deleteWhite(BitMatrix matrix) {
int[] rec = matrix.getEnclosingRectangle();
int resWidth = rec[2] + 1;
int resHeight = rec[3] + 1;
BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
resMatrix.clear();
for (int i = 0; i < resWidth; i++) {
for (int j = 0; j < resHeight; j++) {
if (matrix.get(i + rec[0], j + rec[1]))
resMatrix.set(i, j);
}
}
return resMatrix;
}
}
第三部 controller层
/**
* 生成二维码
* @param type 二维码的类型,为了演示效果,1跳百度的,2是跳优快云
* */
@GetMapping(value = "/create")
@ApiOperation("生成活动详情二维码")
public void getCode(int type , HttpServletResponse response) throws IOException {
// 设置响应流信息
response.setContentType("image/jpg");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
OutputStream stream = response.getOutputStream();
//type是1,生成活动详情、报名的二维码,type是2,生成活动签到的二维码
String content = (type == 1 ? "http://www.baidu.com" : "https://blog.youkuaiyun.com/m0_46991147?spm=1000.2115.3001.5343");
//获取一个二维码图片
BitMatrix bitMatrix = QRCodeUtils.createCode(content);
//以流的形式输出到前端
MatrixToImageWriter.writeToStream(bitMatrix , "jpg" , stream);
}
扫描生成的二维码,就会跳转到百度,如果type给2就会跳转到优快云首页。
在项目中的实际效果:
用户点击生成二维码,将img标签的src属性指向我们的API接口即可,然后这个图片就可以显示出来了。
第四步 下载二维码
function downloadImage(imgsrc, name) {//下载图片地址和图片名
let image = new Image();
// 解决跨域 Canvas 污染问题
image.setAttribute("crossOrigin", "anonymous");
image.onload = function() {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
let url = canvas.toDataURL("image/jpg"); //得到图片的base64编码数据
let a = document.createElement("a"); // 生成一个a元素
let event = new MouseEvent("click"); // 创建一个单击事件
a.download = name || "photo"; // 设置图片名称
a.href = url; // 将生成的URL设置为a.href属性
a.dispatchEvent(event); // 触发a的单击事件
};
image.src = imgsrc;
}
imgSrc就是图片的地址,在这里指向我们上面的API,因为我们的API响应的就是一张图片。
name就是下载之后的文件名。