2017年 的存档
20179 月15

使用Java API绘制验证码图片

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 生成验证码,将验证码绘制成一张图片返回浏览器
 * 
 * @author FreeDroid
 *
 */
public class CheckcodeServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public void service(HttpServletRequest request, 
              HttpServletResponse response) throws 
                     ServletException, IOException {
		/*
		 * step1.绘图
		 */
		Random r = new Random();
		//从getString()方法取出A-Z,0-9的指定长度随机字符串
		String number = getString(5);
		//先创建一个画布(内存映像对象)
		BufferedImage image = new BufferedImage(80, 30, BufferedImage.TYPE_INT_BGR);
		//获得画笔
		Graphics g = image.getGraphics();
		//给画笔设置颜色
		g.setColor(new Color(255, 255, 255));
		//给画布设置背景颜色
		g.fillRect(0, 0, 80, 30);
		//设置字体
		g.setFont(new Font(null, Font.BOLD|Font.ITALIC, 20));
		//给每个字符设置随机颜色,并画到画布上
		for (int i = 0; i < number.length(); i++) {
			//给画笔设置颜色
			g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
			//在画布上画字符
			g.drawString(String.valueOf(number.charAt(i)), i*15, 25);
		}
		//加一些干扰线
		for (int i = 0; i < 8; i++) {
			//给画笔设置颜色
			g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
			//在画布范围内画线
			g.drawLine(r.nextInt(80), r.nextInt(30), r.nextInt(80), r.nextInt(30));
		}
		
		/*
		 * step2.将图片压缩,然后输出
		 */
		//设置响应内容类型为JPEG格式的图片
		response.setContentType("image/jpeg");
		//获得字节输出流(图片是二进制数据)
		OutputStream os = response.getOutputStream();
		//将原始图片(image)按照指定的算法压缩(jpeg)
                //然后将压缩之后得到的字节写入response对象。
		javax.imageio.ImageIO.write(image, "jpeg", os);
		os.close();		
	}

	/**
	 * 随机生成指定长度的大写字母和数字组合
	 * @param length
	 * @return
	 */
	private String getString(int length) {
		StringBuilder str = new StringBuilder();
		Random r = new Random();
		//取出大写字母
		for (int i = 0; i < 26; i++) {
			str.append((char) ('A' + i));
		}
		//取出数字
		for (int i = 0; i < 10; i++) {
			str.append((char) ('0' + i));
		}
		//从字符串str随机取length个字符放进str2组成新的字符串
		StringBuilder str2 = new StringBuilder();
		for (int i = 0; i < length; i++) {
			str2.append(str.charAt(r.nextInt(str.length())));
		}
		return str2.toString();
	}
	
}
20178 月11

UTF-8中文字符编码和解码,用位运算实现

/**
 * UTF中文字符编码和解码
 * 中文字符占3个字节,前缀分别是:1110XXXX 10XXXXXX 10XXXXXX
 * 
 * @author FreeDroid
 *
 */
public class Utf8codeANDdecode {

	public static void main(String[] args) {
		int ch = '我';
		byte[] bytes = codeUTF8(ch);
		char ch2 = decodeUTF8(bytes);
		System.out.println(ch2);
	}

	/**
	 * 解码
	 * @param bytes
	 * @return
	 */
	public static char decodeUTF8(byte[] bytes) {
		int ch = (bytes[0]<<12&0xffff)|(bytes[1]<<6&0x3fff)|(bytes[2]&0x3f);
		return (char) ch;
	}
	
	/**
	 * 编码
	 * @param ch
	 * @return
	 */
	public static byte[] codeUTF8(int ch) {
		int b3 = ch & 0x3f | 0x80;
		int b2 = ch >>> 6 & 0x3f | 0x80;
		int b1 = ch >>> 12 & 0xf | 0xe0;
		return new byte[] { (byte) b1, (byte) b2, (byte) b3 };
	}

}
20177 月30

同名文件自动建立副本递归实现

public void newFile(File file){
	try {
		if (file.createNewFile()) {
			System.out.println("文件创建成功!");
		} else {
			String cName = changeName(file);
			File files = new File("." + File.separator + cName);
			newFile(files);
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}
public String changeName(File file) {
	String name = file.getName();
	int index = name.lastIndexOf('.');
	int index2 = name.lastIndexOf("_副本");
	if (index2 < 0) {
		return name.substring(0, index) + "_副本1" + name.substring(index);
	} else {
		String num = name.substring(index2+3, index);
		int i = Integer.valueOf(num)+1;
		return name.substring(0, index2) + "_副本" + i + name.substring(index);
	}
}
20177 月4

分享:微信电子导航DEMO

后台JAVA代码:

 

package cn.wizzer.app.web.modules.controllers.front.wx;

import cn.wizzer.app.web.commons.base.Globals;
import cn.wizzer.app.wx.modules.services.WxAddressService;
import cn.wizzer.app.wx.modules.services.WxConfigService;
import org.nutz.dao.Cnd;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.weixin.at.impl.MemoryJsapiTicketStore;
import org.nutz.weixin.spi.WxApi2;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * Created by wizzer on 2017/6/27.
 */
@IocBean
@At("/public/wx/add")
public class AddController {
    private static final Log log = Logs.get();
    @Inject
    private WxConfigService wxConfigService;
    @Inject
    private WxAddressService wxAddressService;

    @At("/index/?")
    @Ok("beetl:/public/add/index.html")
    public void index(String wxid,HttpServletRequest req, HttpSession session) {
        WxApi2 wxApi2 = wxConfigService.getWxApi2(wxid);
        if (Lang.isEmpty(Globals.memoryJsapiTicketStore.get(wxid))) {
            Globals.memoryJsapiTicketStore.put(wxid, new MemoryJsapiTicketStore());
        }
        MemoryJsapiTicketStore memoryJsapiTicketStore = Globals.memoryJsapiTicketStore.get(wxid);
        wxApi2.setJsapiTicketStore(memoryJsapiTicketStore);
        String url = "http://" + Globals.AppDomain + Globals.AppBase + "/public/wx/add/index/"+wxid;
        NutMap jsConfig = wxApi2.genJsSDKConfig(url, "getLocation");

        req.setAttribute("list", wxAddressService.query(Cnd.orderBy().asc("opAt")));
        req.setAttribute("jsConfig", Json.toJson(jsConfig));
    }
}

前台代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>电子导航</title>

    <link rel="stylesheet" type="text/css" href="${base!}/assets/public/wx/add/css/css.css"/>
    <link rel="stylesheet" href="${base!}/assets/public/wx/add/css/zepto.mdatetimer.css">
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport"/>
    <meta content="yes" name="apple-mobile-web-app-capable"/>
    <meta content="black" name="apple-mobile-web-app-status-bar-style"/>
    <meta name="format-detection" content="telephone=no"/>
    <meta name="format-detection" content="email=no"/>
    <meta name="msapplication-tap-highlight" content="no">
    <meta charset="utf-8">
    <script type="text/javascript">
        var base = '${base!}';
    </script>
    <script type="text/javascript" src="${base!}/assets/public/wx/add/js/zepto.js"></script>
    <script type="text/javascript" src="${base!}/assets/public/wx/add/js/zepto.mdatetimer.js"></script>
    <script src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>

</head>
<body>
<header class="ds-head">
    <h1>公司检索</h1>
</header>
<div class="time">
    <div class="time-li round-a"><span>公司名称:</span><input id="name"  type="text"
                                                          value=""/></div>
    <div class="btn"><input id="btn" type="button" value="立即查询" class="round-a"/></div>

    <div class="time-li round-a"><span>当前位置:</span><input id="longitude"  type="text"
                                                          value="" readonly/><input id="latitude"  type="text"
                                                                                    value="" readonly/></div>
</div>
<ul class="earn">
    <%for(o in list){%>
    <li class="earn-li">
        <dl>
            <dt class="cf2"><span class="earn-lil">${oLP.index}、${o.name} </span></dt>
            <dd class="cf2"><span class="earn-lil">lng:${o.lng}, lat:${o.lat}</span></dd>
            <dd class="cf2">
                <span><button onclick="goTo('walk','${o.name}','${o.lng}','${o.lat}');">步行</button></span>
                <span><button onclick="goTo('drive','${o.name}','${o.lng}','${o.lat}');">驾车</button></span>
                </dd>
            </dl>
        </li>
    <%}elsefor{%>
    没有检索到结果
    <%}%>
</ul>
<script language="JavaScript">
    function goTo(type,name,lng,lat) {
        var longitude=$("#longitude").val();
        var latitude=$("#latitude").val();
        window.location.href="http://apis.map.qq.com/uri/v1/routeplan?type="+type+"&from=我的位置&fromcoord="+latitude+","+longitude+"&to="+name+"&tocoord="+lat+","+lng+"&policy=1&referer=电子导航";

    //window.location.href="http://api.map.baidu.com/direction?origin=latlng:"+latitude+","+longitude+"|name:我的位置&destination=latlng:"+lat+","+lng+"|name:"+name+"&mode=driving&region=合肥&output=html&src=电子导航";
    }
    wx.config(${jsConfig});
    wx.ready(function(){
        wx.getLocation({
            type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
            success: function (res) {
                var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
                var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
                var speed = res.speed; // 速度,以米/每秒计
                var accuracy = res.accuracy; // 位置精度
                $("#latitude").val(latitude);
                $("#longitude").val(longitude);
            }
        });
    });
</script>
</body>
</html>
20173 月23

NutzWk: 微信AccessToken没有持久化造成超出调用限制的问题解决

1、wx_config 实体类添加三个字段,对应的表结构也要手动修改:


    @Column
    @Comment("access_token")
    @ColDefine(type = ColType.VARCHAR, width = 255)
    private String access_token;

    @Column
    @Comment("access_token_expires")
    @ColDefine(type = ColType.INT)
    private Integer access_token_expires;

    @Column
    @Comment("access_token_lastat")
    @ColDefine(type = ColType.VARCHAR, width = 50)
    private String access_token_lastat;

    get  set ...方法生成出来

2、nutzwx版本升级为1.r.61-SNAPSHOT

        <dependency>
            <groupId>org.nutz</groupId>
            <artifactId>nutzwx</artifactId>
            <version>1.r.61-SNAPSHOT</version>
        </dependency>


3、nutz版本升级为1.r.60

        <dependency>
            <groupId>org.nutz</groupId>
            <artifactId>nutz</artifactId>
            <version>1.r.60</version>
        </dependency>

4、WxConfigService 类getWxApi2替换为如下代码(主要是DaoAccessTokenStore从数据库取access_token)

    public WxApi2 getWxApi2(String wxid) {
        Wx_config appInfo = this.fetch(Cnd.where("id", "=", wxid));
        DaoAccessTokenStore myDaoAccessTokenStore = new DaoAccessTokenStore(dao());
        Map<String, Object> params = new HashMap<>();
        params.put("id", appInfo.getId());
        myDaoAccessTokenStore.setTableAccessToken("access_token");
        myDaoAccessTokenStore.setTableAccessTokenExpires("access_token_expires");
        myDaoAccessTokenStore.setTableAccessTokenLastat("access_token_lastat");
        myDaoAccessTokenStore.setFetch("select access_token,access_token_expires,access_token_lastat from wx_config where id=@id");
        myDaoAccessTokenStore.setUpdate("update wx_config set access_token=@access_token, access_token_expires=@access_token_expires, access_token_lastat=@access_token_lastat where id=@id");
        myDaoAccessTokenStore.setParams(params);
        WxApi2Impl wxApi2 = new WxApi2Impl();
        wxApi2.setAppid(appInfo.getAppid());
        wxApi2.setAppsecret(appInfo.getAppsecret());
        wxApi2.setEncodingAesKey(appInfo.getEncodingAESKey());
        wxApi2.setToken(appInfo.getToken());
        wxApi2.setAccessTokenStore(myDaoAccessTokenStore);
        return wxApi2;
    }
20173 月17

log4j 换成 logback 注意事项

pom.xml

 

<dependency>  
    <groupId>ch.qos.logback</groupId>  
    <artifactId>logback-classic</artifactId>  
    <version>1.2.2</version>  
</dependency>  
<dependency>  
    <groupId>org.logback-extensions</groupId>  
    <artifactId>logback-ext-spring</artifactId>  
    <version>0.1.4</version>  
</dependency>  
<dependency>  
    <groupId>org.slf4j</groupId>  
    <artifactId>jcl-over-slf4j</artifactId>  
    <version>1.7.25</version>  
</dependency>
<dependency>  
    <groupId>org.slf4j</groupId>  
    <artifactId>log4j-over-slf4j</artifactId>  
    <version>1.7.25</version>  
</dependency>

 

排除其他包的log4j引用,特别是dubbo的、shiro的

 

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.8.4</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.3.2</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.3.2</version>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

配置文件:

<?xml version="1.0" encoding="UTF-8"?>

<!-- For assistance related to logback-translator or configuration  -->
<!-- files in general, please contact the logback user mailing list -->
<!-- at http://www.qos.ch/mailman/listinfo/logback-user             -->
<!--                                                                -->
<!-- For professional support please see                            -->
<!--    http://www.qos.ch/shop/products/professionalSupport         -->
<!--                                                                -->
<configuration>
    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
        <file>/monitor.log</file>

        <!-- Policy定义如何滚动,按文件大小滚动生成日志 -->
        <!-- 如果是按文件大小滚动生成日志,前面的file标签可省略,而使用fileNamePattern标签定义的名字 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <fileNamePattern>/monitor.%i.log.gz
            </fileNamePattern>
            <!-- 归档日志的下标,替换fileNamePattern的%i,最多3个归档文件 -->
            <minIndex>1</minIndex>
            <maxIndex>5</maxIndex>
        </rollingPolicy>

        <!-- triggeringPolicy定义什么时候滚动,下面是定义了文件大小超过100M的时候产生归档文件 -->
        <triggeringPolicy
                class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>300MB</maxFileSize>
        </triggeringPolicy>

        <!-- append是否接着上次写文件结尾继续写,默认为true -->
        <append>true</append>
        <encoding>GBK</encoding>

        <!-- layout,定义格式 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[%-5level] %d{HH:mm:ss.SSS} [%thread] %logger %caller{2} - %msg%n</pattern>
        </layout>
    </appender>
    <logger name="org.springframework" level="info"/>
    <logger name="org.nutz.dao" level="debug"/>
    <logger name="net.sf.ehcache" level="info"/>
    <logger name="druid.sql" level="info"/>
    <logger name="com.alibaba.druid" level="info"/>
    <logger name="org.apache.shiro" level="info"/>
    <logger name="org.quartz" level="info"/>
    <root level="debug">
        <appender-ref ref="Console"/>
    </root>
</configuration>
20173 月8

怀孕

“医生,你得救救我们啊,我们在一起三年了都没有怀孕,父母还等着抱孙子呢!”医生:“这我也没办法啊,这都是你们自己的问题。”“不行啊,医生你帮帮我们吧。”医生:“首先,你们得各自找个女朋友。”

20171 月12

培训班

记得初入公司时,写的代码乱七八糟,错误百出,bug连连,不仅项目经理骂我,其他同事也对我怨声载道。后来听朋友介绍,就报了一个培训班。经过1个月的刻苦学习,终于功夫不负有心人啊—他们都骂不过我了。