本周预告了将会推出一个「将会讲讲项目中关于微信二维码生成和传播的事儿」的系列,现在我们就来开始讲讲;
首先本篇不会将会怎么调用二维码接口,因为该接口已经在上个月就写好了,在实际项目中,不过这个是微信二维码1,在2中我会讲讲;
项目之前这个模块的状态是,已经获取到会员的二维码图片,我开始是将其放置到一个网页当中,客户提出需要直接将该网页生成一张可以下载保存的图片,以便会员进行推广,具体的推广方式我简单描述一下,因为这个可能所有想传播自己的服务号或者说是客户想用「变向传销」的方式拉会员的话,可能都要走这一步,当前应用注册的会员可以通过二维码传播到自己的朋友圈,其实大家都知道,二维码无非就是一个url(正常情况),其中我们简单的加了一个用户的id,那么他传播出去之后,他的朋友扫码或者自动识别之后微信就把这个扫码的用户推送到服务号的界面,这个时候我们记录了这种关联关系,即传播会员和扫码待发展的用户,如果用户进入微官网,并进入注册页面,那么我们就自动将待发展会员的介绍人识别成传播者,那么注册成功之后,我们将会返奖给传播者。
接下来先看看实际的页面:
这个是获取会员自己的推广码的入口;
这个是没有改之前的效果(吐槽一下这个应用的ui设计不是我做的,是老板自己,不是我做的哈 - - 。。。。);
而目前这种状态在微信上,长按页面之后无法完成上面的需求,所以下面就来改呗,目的就是把这个网页,截屏并存储到服务器,之后将页面内容替换成截屏后的图片,那么会员就可以方便的进行保存并传播;
下面先看页面的jsp代码:
WEB-INF/jsp/foreground/puser/myQRCode.jsp
<span style="font-size:12px;"><span style="font-size:12px;"><%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<!DOCTYPE html>
<html>
<head>
<jsp:include page="/WEB-INF/jsp/foreground/include.jsp">
<jsp:param name="pageTitle" value="边玩边赚钱,一起乐秀吧!"/>
<jsp:param name="needScojsStyle4Valid" value="true"/>
</jsp:include>
<style>
#pageBody{
background-color: #000000;
}
.pageContainer {
margin-top: 51px;
margin-bottom: 10px;
min-height: 150px;
}
.formInput, textarea, input[type="text"], input[type="password"], input[type="tel"], input[type="email"], input[type="number"], input[type="search"], select[multiple], select[size], input[type="date"] {
width: 55%;
}
.form-horizontal .radio-inline {
padding-left: 30px;
margin-left: 0px;
min-width: 130px;
min-height: 50px;
line-height: 16px;
font-size: 22px;
}
#qrBox {
margin-top: 12%;
background-image: url('<%=request.getContextPath()%>/resources/img/qrcode_bg.jpg');
padding-top: 21%;
background-size: 100%;
background-repeat: no-repeat;
}
#qrInnerBox{
padding:16% 0px 0% 0px
}
#qrInnerBox2{
padding:0% 0px 45% 0px
}
#qrimg {
width: 49%;
margin: 0 auto;
display: inherit;
}
@media (min-width: 768px) {
#qrBox {
padding-top: 17%;
margin: 0 auto;
}
#qrInnerBox{
padding: 30% 0px 0% 0px
}
#qrInnerBox2{
padding:0% 0px 48% 0px
}
.control-group {
margin: 0 auto;
width: 560px;
}
}
</style>
</head>
<body id="pageBody">
<jsp:include page="/WEB-INF/jsp/foreground/commnav.jsp">
<jsp:param name="pageTitle" value="${pageTitle}"/>
</jsp:include>
<!-- /container -->
<div class="container" id="qrBox">
<div class="row" class="form-group" style="">
<img id="qrimg" data-path="${PUser.QRcodeImgPath}" src="<%=request.getContextPath()%>${PUser.QRcodeImgPath}"
alt="长按此图,识别二维码,一起玩转乐秀吧!">
</div>
<div class="row" style="" id="qrInnerBox">
<div class="col-xs-4" style="height: 80px"></div>
<div class="col-xs-4" style="height: 80px">
<img class="img-circle center-block" id="userPhotoImg" style=" width: 80px;height: 80px;"
src="${PUser.photoPath}">
</div>
<div class="col-xs-4" style="height: 80px"></div>
</div>
<div class="row" id="qrInnerBox2"><h4 style="text-align: center;color: red"><span style="font-size: 14px;color: #000000">我是</span> ${PUser.nickName}</h4></div>
</div>
<!-- other -->
<jsp:include page="/WEB-INF/jsp/foreground/footer.jsp">
<jsp:param name="intertBefoePageInitScript" value="resources/js/lib/html2canvas/dist/html2canvas.js"/>
<jsp:param name="pageInitScript" value="resources/js/foreground/puser/myQRCode.js"/>
<jsp:param name="needWeiChartApi" value="true"/>
<jsp:param name="notShow" value="true"/>
</jsp:include>
<!-- /other -->
<c:if test="${empty loginPuser.id}">
<c:if test="${not empty param.introducerUsername}">
<jsp:include page="/WEB-INF/jsp/compent/introducerFixedCompent.jsp" >
<jsp:param name="introducerUsername" value="${param.introducerUsername}"/>
</jsp:include>
</c:if>
</c:if>
</body>
</html></span></span>
读过我上一篇 微信红包接入2-项目集成 的朋友可能对我这个应用架构有所了解,这里我就不讲了,其实这个页面很简单,可以看下面这个在线的连接,http://lexiuba.com/puser/myQRCode/2903?introducerUsername=jin,在进入这个页面的action中已经获取或者将之前获取到的自定义二维码(微信生成的),嵌入到页面中,附加了一个背景图片和会员的基本信息;
下面看看关联的脚本:
resources/js/foreground/puser/myQRCode.js
<span style="font-size:12px;"><span style="font-size:12px;">/**
* Created by zhaojin on 15/5/4.
*/
gloablObj.option.pageInit = function (memberId) {
gloablObj.interaction.doSomeThing(function () {
html2canvas(document.getElementById("qrBox"), {
onrendered: function (canvas) {
$(canvas).attr({id: 'canvas'})
document.body.appendChild(canvas);
$('#qrBox').fadeOut();
var canvas = document.getElementById('canvas');
var filePath = $('#qrimg').data('path').split('/');
var fileNameArr = filePath[filePath.length - 1].split('.');
var fileName = fileNameArr[0] + '_Canvas.png';
gloablObj.interaction.doPost(
gloablObj.dict.APP_CONTEXT + '/puser/uploadQRCodeCanvas',
{
fileData: canvas.toDataURL(),
fileName: fileName
},
function (res, msg) {
var resImg = $('<img />').attr({src:gloablObj.dict.APP_CONTEXT+msg}).css({
width: '100%',
height: '100%',
'margin-top': '50px'
});
$('body').prepend(resImg);
$('#canvas').fadeOut();
$.scojs_message('长按二维码图片即可保存到手机,赶快推广吧!', $.scojs_message.TYPE_OK);
},false)
}
});
}, 500, false)
};
</span></span>
这里是重点,网页截屏,听着就好高级的样子,其实万能的开源社区github已经有人开源了一个用javascript写的截屏框架:https://github.com/niklasvh/html2canvas/releases,就是借助它我们一个函数就完成了对应的工作;
搜先必须在jsp中导入这个框架核心脚本:
<span style="font-size:12px;"><span style="font-size:12px;"><jsp:param name="intertBefoePageInitScript" value="resources/js/lib/html2canvas/dist/html2canvas.js"/></span></span>
接着,就可以在页面初始化完毕之后,直接调用截屏函数:
<span style="font-size:12px;"><span style="font-size:12px;">html2canvas(element, options);</span></span>
至于原理框架作者已经做的很清楚了,而且还谦虚的说,让我们不要用在生成环境,他只是做着玩玩,老外好谦虚的哈哈:
<span style="font-size:12px;"><span style="font-size:12px;">How it works
The script traverses through the DOM of the page it is loaded on. It gathers information on all the elements there, which it then uses to build a representation of the page. In other words, it does not actually take a screenshot of the page, but builds a representation of it based on the properties it reads from the DOM.
As a result, it is only able to render correctly properties that it understands, meaning there are many CSS properties which do not work.</span></span>
下面我们先看后台怎么来接收截屏后的图像数据,再来说说这个函数里面的内容:
<span style="font-size:12px;"><span style="font-size:12px;">/**
* @param fileData Base64编码过的图片数据信息,对字节数组字符串进行Base64解码
*/
@AuthMethod(pageTitle = "缓存我的推广码截屏")
@RequestMapping(value = "/uploadQRCodeCanvas", method = RequestMethod.POST)
@ResponseBody
public String uploadQRCodeCanvas(@RequestParam(required = true) String fileData,
@RequestParam(required = true) String fileName,
HttpServletRequest httpServletRequest) {
logger.error(fileData);
String realPath = httpServletRequest.getSession().getServletContext().getRealPath(Dict.MEMBER_QRCODE_UPLOAD_FOLDER);
File rf = new File(realPath);
if (!rf.exists()) {
rf.mkdirs();
}
String filePathStr = realPath + "/" + fileName;
File filePath = new File(filePathStr);
//判断服务器中是否已经存储了当前的二维码网页截屏,如果已经存在直接返回对应图片地址:
AjaxObj ao = null;
if(filePath.exists()){
ao = new AjaxObj(AjaxObj.SUCCESS, Dict.MEMBER_QRCODE_UPLOAD_FOLDER+ "/" + fileName);
}else {
try {
File saveFile = uploadFile(fileData, filePath);
if (ImageUtil.isImage(saveFile)) {
ao = new AjaxObj(AjaxObj.SUCCESS, Dict.MEMBER_QRCODE_UPLOAD_FOLDER+ "/" + fileName);
} else {
saveFile.delete();
throw new CommonException(DictError.FAIL_UPLOADIMG_ISERROE);
}
} catch (IOException e) {
e.printStackTrace();
throw new CommonException(DictError.FAIL_UPLOADIMG_ISERROE);
}
}
return JsonUtil.getInstance().obj2json(ao);
}</span></span>
接口需要两个东东,一就是图片数据,而html5 canvas给了我们一个接口:toDataURL,这个接口返回了指定类型的转换图形数据,以Base64的方式进行编码,而在解码的时候:
<span style="font-size:12px;"><span style="font-size:12px;"> /**
* 文件上传
*
* @param fileData
* @param filePath
* @return
*/
private File uploadFile(String fileData, File filePath) throws IOException {
fileData = fileData.replace("data:image/png;base64,", "");
//使用BASE64对图片文件数据进行解码操作
BASE64Decoder decoder = new BASE64Decoder();
//通过Base64解密,将图片数据解密成字节数组
byte[] bytes = decoder.decodeBuffer(fileData);
//构造字节数组输入流
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
//读取输入流的数据
BufferedImage bi = ImageIO.read(bais);
//将数据信息写进图片文件中
ImageIO.write(bi, "png", filePath);// 不管输出什么格式图片,此处不需改动
bais.close();
return filePath;
}</span></span>
需要注意的一个地方就是:
1. 将fileData中不要的字符串干掉
2.你前台指定传输的是什么图片类型,后台在write的时候存储的就需要是对应的类型,不然存储的就是黑色的图片,那就是存储编码错了;
而交易本身很简单,存储截屏图片到指定路径,再把图片相对路径返回给前台,前台回调函数中创建一个img,插入到body中作为一级节点,操作ui,影藏不必要的组件,结果就玩咯;
时间和其他原因就写到这了,有疑问的朋友可以留言讨论;
![](https://img-my.csdn.net/uploads/201507/27/1437983673_1697.jpeg)