本篇将主要介绍如何生成一个既含LOGO又含底部文字的二维码.
先展示一下最终成果图,如下:
好,现在来说一说具体的实现思路.
第一步,实现LOGO图片的上传:
①LOGO文件选择框改变事件触发后,将文件传输到后台
②后台存储后,将文件于服务器端的实际存储地址返回个前端
③前端将文件地址进行存储
第二步,实现二维码的生成:
①生成二维码按钮点击事件触发后,将“二维码信息”、“LOGO地址”、“底部字符”一并传递到后台
②后台检验“LOGO地址”和“底部字符”是否存在,存在则插入二维码一并生成
第三步,前端进行二维码的展示
①前端AJAX的success回调函数中,获取到二维码的Base64字符串
②将此Base64字符串直接放入图片展示区域<img>标签的src属性中即可正确显示创造的二维码
现在来看具体的实现代码(实现代码中有大量的代码注释供理解所用):
1. 首先引入文件上传、二维码生成以及JSONObject对象所依赖的包
<!-- 文件上传 -->
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<!-- 二维码生成 -->
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.0</version>
</dependency>
<!-- JSONObject对象依赖 -->
<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
</dependency>
2.在applicationContext.xml中,配置文件解析器,如下图:
<!-- 文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为1MB -->
<property name="maxUploadSize" value="1048576" />
<!-- 字符编码 -->
<property name="defaultEncoding" value="UTF-8" />
</bean>
3.创建文件上传的控制器FileController,并写好LOGO图片上传的方法.
/**
* TODO
*
* @author ShangHai
* @date 2019/12/02 16:40
*/
@Controller
@RequestMapping(value= "/file")
public class FileController {
/**
* 此方法用于将用户上传的图片保存在指定位置
* @param file 用户上传的LOGO图片
* @return
*/
@ResponseBody
@RequestMapping(value= "/loGoUpload",produces = {"text/html;charset=UTF-8"})
public String loGoUpload(@RequestParam(value = "file",required = false)MultipartFile file){
JSONObject jsonObject = new JSONObject();
try{
//再次判断文件是否为空
if(file.isEmpty()){
jsonObject.put("status","fail");
jsonObject.put("msg","上传的文件为空,请重新选择");
return jsonObject.toString();
}
//声明图片存放路径
String path = "G:"+File.separator+"study"+File.separator+"upload";
//获取上传文件名
String fileName = file.getOriginalFilename();
//获取文件的类型
String fileType = fileName.substring(fileName.lastIndexOf("."));
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
//对上传的图片进行重命名:命名规则为 "随机的UUID + 年月日时分"
String newFileName = UUID.randomUUID()+"-"+sdf.format(new Date())+fileType;
//实例化此路径
File targetFile = new File(path+File.separator+newFileName);
if(!targetFile.exists()){
//文件若不存在,则进行创建
//若文件所属的父目录均不存在时,mkdirs方法会连父目录一起创建
targetFile.mkdirs();
}
//拷贝
file.transferTo(targetFile);
jsonObject.put("status","ok");
jsonObject.put("msg","文件上传成功");
//注意此处!!将图片在服务器上的实际存储地址传给前端
jsonObject.put("logoPath",""+path+File.separator+newFileName);
}catch(Exception e){
jsonObject.put("status","error");
jsonObject.put("msg","发生了未知的错误,请联系管理员");
}
return jsonObject.toString();
}
}
ps:需要将图片的实际地址传递给前端
//注意此处!!将图片在服务器上的实际存储地址传给前端
jsonObject.put("logoPath",""+path+File.separator+newFileName);
4.创建一个二维码生成的工具类,QRCodeUtil.
/**
* TODO
*
* @author ShangHai
* @date 2019/12/04 22:08
*/
public class QRCodeUtil {
/**
* 生成二维码图片并返回
* @param content 二维码存储的内容
* @param width 指定二维码的宽度
* @param height 指定二维码的高度
* @return
* @throws Exception
*/
public static BufferedImage createQrCodeImage(String content,int width,int height) throws Exception{
Hashtable hints = new Hashtable();
//指定纠错等级
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//生成二维码矩阵
BitMatrix bitMatrix =
new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
//获取矩阵宽度、高度,生成二维码图片
BufferedImage image = new BufferedImage(bitMatrix.getWidth(),bitMatrix.getHeight(),BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
return image;
}
/**
* 往二维码中插入一个LOGO,也可以在底部插入一行字符
* @param image 需要插入LOGO的二维码
* @param logoPath LOGO的路径
* @param showBottomText 底部需要显示的字符
* @return
* @throws IOException
*/
public static BufferedImage megerLoGo(BufferedImage image,String logoPath,String showBottomText) throws IOException{
Graphics2D gs = image.createGraphics();
//1.加入LOGO水印效果
if(logoPath!=null && !"".equals(logoPath)){
//1.1 载入LOGO图片
BufferedImage logoImage = ImageIO.read(new File(logoPath));
//1.2 考虑到LOGO图片将插入到二维码中间,建议大小不要超过二维码的1/5
int width = image.getWidth()/4;
int height = image.getHeight()/4;
//1.3 LOGO居中显示
int x = (image.getWidth() - width)/2;
int y = (image.getHeight() - height)/2;
gs.drawImage(logoImage,x,y,width,height,null);
logoImage.flush();
}
//2.二维码图片底部插入文字
if(showBottomText!=null && !"".equals(showBottomText)){
//2.1 设置字体样式
Font font = new Font("微软雅黑", Font.PLAIN, 14);
gs.setColor(Color.BLACK);
gs.setFont(font);
//2.2 字体显示位置
int x = (image.getWidth() - getWatermarkLength(showBottomText, gs))/2;
int y = image.getHeight()-10;
gs.drawString(showBottomText, x, y);
}
//销毁图形界面资源
gs.dispose();
return image;
}
/**
* 将图片转换成base64的字符串并返回
* @param image 需要进行转换的图片
* @return
* @throws Exception
*/
public static String imageToBase64(BufferedImage image) throws Exception{
String base64 = "data:image/jpg;base64,";
//新建字符数组输出流
ByteArrayOutputStream os = new ByteArrayOutputStream();
//利用ImageIO类提供的write方法,将图片以jpg的数据模式写入字符数组输出流
ImageIO.write(image,"jpg",os);
//从流中获取数据数组
byte[] bytes = os.toByteArray();
//将数据数组转换成base64字符串
base64 += new BASE64Encoder().encode(bytes);
return base64;
}
/**
* 获取水印字体的长度
* @param fontString
* @param gs
* @return
*/
public static int getWatermarkLength(String fontString,Graphics2D gs){
return gs.getFontMetrics(gs.getFont()).charsWidth(fontString.toCharArray(),0,fontString.length());
}
}
5.创建条码生成控制器CodeController.
/**
* TODO
*
* @author ShangHai
* @date 2019/12/04 22:16
*/
@Controller
@RequestMapping(value= "/code")
public class CodeController {
/**
* 根据用户输入的内容生成二维码,并将二维码转换为base64字符串并返回
* @param content 二维码存储的信息
* @param logoPath LOGO的存储地址
* @param bottomText 底部字符
* @return
*/
@ResponseBody
@RequestMapping(value= "/getQrCodeBase64",produces = {"text/html;charset=utf-8"})
public String getQrCodeBase64(String content,String logoPath,String bottomText){
JSONObject jsonObject = new JSONObject();
try {
//获取二维码图片
BufferedImage image = QRCodeUtil.createQrCodeImage(content,200,200);
//往二维码图片插入LOGO及字符
image = QRCodeUtil.megerLoGo(image,logoPath,bottomText);
//二维码图片转base64字符串
String base64 = QRCodeUtil.imageToBase64(image);
jsonObject.put("status","ok");
//将得到的base64字符串放入jsonObject对象中,方便转换成json数据
jsonObject.put("base64",base64);
} catch (Exception e) {
jsonObject.put("status","fail");
jsonObject.put("base64","");
e.printStackTrace();
}
return jsonObject.toString();
}
}
至此,后台的处理方法均已完善.
6.创建前端展示界面QRCode_Base64.jsp.
<style type="text/css">
*{margin: 0;padding: 0;list-style: none;}
.container{width: 360px;height: 430px;margin: auto;margin-top: 100px;
box-shadow: 0 0 20px black;overflow: hidden;}
.container .title{width: 100%;height: 50px;outline: 1px solid black;
text-align: center;font-weight: bold;font-size: 18px;line-height: 50px;}
.container .code_content{width: 100%;height: 35px;outline: 1px solid silver;
line-height: 35px;padding-left: 10px;}
.container .logo_upload{width: 100%;height: 35px;outline: 1px solid silver;
line-height: 35px;padding-left: 10px;}
.container .bottom_text{width: 100%;height: 35px;outline: 1px solid silver;
line-height: 35px;padding-left: 10px;}
.container .operation{width: 100%;height: 35px;outline: 1px solid silver;
line-height: 35px;padding-left: 10px;}
.container .img_show{width: 100%;height: 200px;text-align: center;}
.container .img_show div{font-size: 14px;font-weight: bold;margin-top: 10px;}
</style>
<body>
<div class="container">
<div class="title">二维码生成器</div>
<div class="code_content">
二维码信息输入:
<input type="text" id="codeContent" name="codeContent">
</div>
<div class="logo_upload">
嵌入LOGO:
<input type="file" id="logoImg" name="logoImg">
<input type="hidden" id="logoPath" name="logoPath">
</div>
<div class="bottom_text">
底部字符输入:
<input type="text" id="bottomText" name="bottomText">
</div>
<div class="operation">
操作:
<button id="submitBtn" name="submitBtn">生成二维码</button>
</div>
<div class="img_show">
<div>二维码的显示区域</div>
<center>
<img id="imgShow" name="imgShow" style="display: none;">
</center>
</div>
</div>
</body>
7.LOGO文件框改变事件绑定的JQuery逻辑代码:
//LOGO图片文件上传
$("#logoImg").change(function () {
//初始化:LOGO图片上传后保存的位置
$("#logoPath").val("");
//获取图片的路径
var filePath = $("#logoImg").val();
if(filePath == ""){
alert("请选择一张图片");
$("#logoImg").val("");
return ;
}
//截取图片文件类型
var fileType = filePath.substring(filePath.lastIndexOf("."));
if(fileType!=".jpg" && fileType!=".png" && fileType!=".bmp" && fileType!=".gif"){
alert("请选择一张图片");
$("#logoImg").val("");
return ;
}
//生成FormData,用于装上传的图片文件
var formData = new FormData();
formData.append("file",$('#logoImg')[0].files[0]);
// 通过ajax技术上传图片文件,
// 成功上传后,获取图片保存的路径,并存于页面的隐藏域中
$.ajax({
url: '/file/loGoUpload', //图片上传的控制层映射
dataType: 'json', //返回值类型为json
data: formData,
type: 'POST',
processData: false, //使数据不做处理
contentType: false, //不要设置Content-Type请求头
success: function (data) { //data为返回的json数据
if(data.status == 'ok'){
alert('图片上传成功!');
//关键点:将图片上传成功后保存的实际地址记录下来
$("#logoPath").val(data.logoPath);
} else {
alert('图片上传失败!');
}
},error: function () {
alert('ajax请求失败');
}
});
});
生成二维码按钮点击事件绑定的JQuery逻辑代码:
//获取二维码图片
$("#submitBtn").click(function () {
//初始化:将图片展示区域隐藏、src清空
$("#imgShow").css("display","none");
$("#imgShow").attr("src","");
//获取输入的二维码内容
var content = $("#codeContent").val();
//LOGO图片的存放路径
var logoPath = $("#logoPath").val();
//底部字符
var bottomText = $("#bottomText").val();
if(content == ""){
alert("请输入二维码需要存储的内容");
$("#codeContent").focus();
return ;
}
//通过ajax技术访问后台,获取返回的base64字符串.
//成功获取后,将base64字符串直接放入img标签的src属性中即可
$.ajax({
url: "/code/getQrCodeBase64", //二维码生成的映射路径
data: {
content: content, //二维码内容
logoPath: logoPath, //LOGO图片存储地址
bottomText: bottomText //底部字符
},
type: 'POST',
dataType: 'json', //返回数据类型为json类型
success: function (data) { //请求成功回调函数
if(data.status == "ok"){
alert("获取成功");
//图片展示区域显示并将base64字符串放入src中即可
$("#imgShow").css("display","block");
$("#imgShow").attr("src",data.base64);
} else {
alert("获取失败");
$("#codeContent").val("");
$("#codeContent").focus();
}
},error: function () { //请求失败回调函数
alert("ajax请求失败");
}
});
});
至此,整个二维码生成器便实现了.
以下是QRCode_Base64.jsp页面所有的代码:
<%--
Created by IntelliJ IDEA.
User: ShangHai
Date: 2019/12/04
Time: 22:24
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>QRCode_Base64</title>
<style type="text/css">
*{margin: 0;padding: 0;list-style: none;}
.container{width: 360px;height: 430px;margin: auto;margin-top: 100px;
box-shadow: 0 0 20px black;overflow: hidden;}
.container .title{width: 100%;height: 50px;outline: 1px solid black;
text-align: center;font-weight: bold;font-size: 18px;line-height: 50px;}
.container .code_content{width: 100%;height: 35px;outline: 1px solid silver;
line-height: 35px;padding-left: 10px;}
.container .logo_upload{width: 100%;height: 35px;outline: 1px solid silver;
line-height: 35px;padding-left: 10px;}
.container .bottom_text{width: 100%;height: 35px;outline: 1px solid silver;
line-height: 35px;padding-left: 10px;}
.container .operation{width: 100%;height: 35px;outline: 1px solid silver;
line-height: 35px;padding-left: 10px;}
.container .img_show{width: 100%;height: 200px;text-align: center;}
.container .img_show div{font-size: 14px;font-weight: bold;margin-top: 10px;}
</style>
<script src="${pageContext.request.contextPath}/lib/jquery-2.1.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
//让二维码信息输入框获取焦点
$("#codeContent").focus();
//LOGO图片文件上传
$("#logoImg").change(function () {
//初始化:LOGO图片上传后保存的位置
$("#logoPath").val("");
//获取图片的路径
var filePath = $("#logoImg").val();
if(filePath == ""){
alert("请选择一张图片");
$("#logoImg").val("");
return ;
}
//截取图片文件类型
var fileType = filePath.substring(filePath.lastIndexOf("."));
if(fileType!=".jpg" && fileType!=".png" && fileType!=".bmp" && fileType!=".gif"){
alert("请选择一张图片");
$("#logoImg").val("");
return ;
}
//生成FormData,用于装上传的图片文件
var formData = new FormData();
formData.append("file",$('#logoImg')[0].files[0]);
// 通过ajax技术上传图片文件,
// 成功上传后,获取图片保存的路径,并存于页面的隐藏域中
$.ajax({
url: '/file/loGoUpload', //图片上传的控制层映射
dataType: 'json', //返回值类型为json
data: formData,
type: 'POST',
processData: false, //使数据不做处理
contentType: false, //不要设置Content-Type请求头
success: function (data) { //data为返回的json数据
if(data.status == 'ok'){
alert('图片上传成功!');
//关键点:将图片上传成功后保存的实际地址记录下来
$("#logoPath").val(data.logoPath);
} else {
alert('图片上传失败!');
}
},error: function () {
alert('ajax请求失败');
}
});
});
//获取二维码图片
$("#submitBtn").click(function () {
//初始化:将图片展示区域隐藏、src清空
$("#imgShow").css("display","none");
$("#imgShow").attr("src","");
//获取输入的二维码内容
var content = $("#codeContent").val();
//LOGO图片的存放路径
var logoPath = $("#logoPath").val();
//底部字符
var bottomText = $("#bottomText").val();
if(content == ""){
alert("请输入二维码需要存储的内容");
$("#codeContent").focus();
return ;
}
//通过ajax技术访问后台,获取返回的base64字符串.
//成功获取后,将base64字符串直接放入img标签的src属性中即可
$.ajax({
url: "/code/getQrCodeBase64",
data: {
content: content,
logoPath: logoPath,
bottomText: bottomText
},
type: 'POST',
dataType: 'json',
success: function (data) {
if(data.status == "ok"){
alert("获取成功");
//图片展示区域显示并将base64字符串放入src中即可
$("#imgShow").css("display","block");
$("#imgShow").attr("src",data.base64);
} else {
alert("获取失败");
$("#codeContent").val("");
$("#codeContent").focus();
}
},error: function () {
alert("ajax请求失败");
}
});
});
});
</script>
</head>
<body>
<div class="container">
<div class="title">二维码生成器</div>
<div class="code_content">
二维码信息输入:
<input type="text" id="codeContent" name="codeContent">
</div>
<div class="logo_upload">
嵌入LOGO:
<input type="file" id="logoImg" name="logoImg">
<input type="hidden" id="logoPath" name="logoPath">
</div>
<div class="bottom_text">
底部字符输入:
<input type="text" id="bottomText" name="bottomText">
</div>
<div class="operation">
操作:
<button id="submitBtn" name="submitBtn">生成二维码</button>
</div>
<div class="img_show">
<div>二维码的显示区域</div>
<center>
<img id="imgShow" name="imgShow" style="display: none;">
</center>
</div>
</div>
</body>
</html>
即可进行测试!
第一种情况:既不插入LOGO图片,也不插入底部字符.
第二种情况:仅插入LOGO图片,不插入底部字符.
选择一张LOGO图片:
然后点击"生成二维码"按钮
第三种情况:既插入LOGO图片,又插入底部字符.
选择一张LOGO图片,同上.然后输入一串底部字符
没有任何问题,完美实现!