├── src ├── main │ ├── resources │ │ └── application.properties │ └── java │ │ └── me │ │ └── xueyao │ │ ├── README.md │ │ ├── file │ │ ├── DirUtils.java │ │ ├── ZipUitls.java │ │ ├── FileUtilsSIMON.java │ │ └── CompressUtils.java │ │ ├── SimonJavaUtilsApplication.java │ │ ├── response │ │ ├── ResponseStatus.java │ │ ├── PagedResponse.java │ │ ├── BaseResponse.java │ │ ├── R.java │ │ └── ResultPage.java │ │ ├── convert │ │ ├── ConvertUtils.java │ │ └── BeanMapConvert.java │ │ ├── encryption │ │ ├── MD5Utils.java │ │ ├── AESUtils.java │ │ ├── DESUtils.java │ │ ├── MD5.java │ │ ├── Base64Utils.java │ │ ├── SecurityUtils.java │ │ └── Base64.java │ │ ├── BeanCompareUtils.java │ │ ├── GenerateSequenceUtil.java │ │ ├── http │ │ ├── CookieUtils.java │ │ ├── MailQQUtils.java │ │ ├── HttpIpAddress.java │ │ ├── HttpHelper.java │ │ └── HttpRequestUtils.java │ │ ├── tabooed │ │ ├── TabooedUtils.java │ │ ├── TabooedWords.java │ │ ├── SensitiveWordInit.java │ │ ├── SensitivewordFilterUtil.java │ │ └── TabooedTools.java │ │ ├── CompositeComparator.java │ │ ├── collection │ │ ├── CloneUtils.java │ │ ├── BigDecimalUtils.java │ │ ├── ArrayUtils.java │ │ ├── StringUtil.java │ │ └── ConvertUtils.java │ │ ├── format │ │ ├── TimestampUtils.java │ │ ├── MathUtils.java │ │ └── MoneyUtils.java │ │ ├── cache │ │ ├── ConcurrentHashMapUtil.java │ │ └── LRUPlusCache.java │ │ ├── SimonTreeUtil.java │ │ ├── validate │ │ ├── ValidateHelper.java │ │ ├── IdcardValidator.java │ │ ├── RegexUtils.java │ │ └── Validators.java │ │ ├── RandomUtils.java │ │ ├── date │ │ ├── DateFormatUtils.java │ │ └── TimeUtil.java │ │ ├── IdWorker.java │ │ └── map │ │ └── Geohash.java └── test │ └── java │ └── me │ └── xueyao │ └── SimonJavaUtilsApplicationTests.java ├── README.md ├── .gitignore └── pom.xml /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simon Java Utils 2 | ## 常用的Java工具类 3 | > 因为某些工具需要第三方包,所以项目为Maven,具体可以看src目录下的文件 4 | 5 | ### 更新日志 6 | * 2020-03-01 把Awesome-Java-Utils项目中的工具类移到这个项目中 7 | * 2020-03-02 新增地图工具类 8 | * 2020-03-26 用ConcurrentHashMap实现带有过期时间的缓存 9 | * 2020-11-22 新增谷歌翻译 10 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/README.md: -------------------------------------------------------------------------------- 1 | ## 工具类目录如下 2 | * collection - 集合工具类 3 | * convert - 转换工具类 4 | * date - 时间工具类 5 | * encryption - 加密工具类 6 | * file - 文件工具类 7 | * format - 格式化工具类 8 | * http - 网络相关工具类 9 | * image - 图片相关工具类 10 | * tabooed - 敏感词过滤 11 | * validate - 校验工具类 12 | * map - 地图工具类 -------------------------------------------------------------------------------- /src/test/java/me/xueyao/SimonJavaUtilsApplicationTests.java: -------------------------------------------------------------------------------- 1 | package me.xueyao; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SimonJavaUtilsApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/file/DirUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.file; 2 | /** 3 | * 根据文件名目录打散工具类 4 | */ 5 | public class DirUtils { 6 | public static String getDir(String name) { 7 | if (name != null) { 8 | int code = name.hashCode(); 9 | return "/" + (code & 15) + "/" + (code >>> 4 & 15); 10 | } 11 | return null; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/SimonJavaUtilsApplication.java: -------------------------------------------------------------------------------- 1 | package me.xueyao; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SimonJavaUtilsApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SimonJavaUtilsApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/response/ResponseStatus.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.response; 2 | 3 | /** 4 | * 响应的枚举类 5 | * @author simonxue 6 | */ 7 | public enum ResponseStatus { 8 | SUCCESS(10000, "成功"), 9 | INVALID_SIGNATURE(20002, "无效签名"), 10 | SEND_SMS_MAXIMUM(20003, "短信发送次数达到上限"), 11 | SEND_SMS_FAIL(20004,"短信发送失败"), 12 | BAD_REQUEST(40000,"请求有误"), 13 | BAD_PARAM(40001, "参数错误"), 14 | UNAUTHORIZED(40401, "用户未身份认证"), 15 | NO_PERMISSION(40403, "没有接口权限"), 16 | NOT_FOUND(40404,"您所访问的资源不存在"), 17 | Method_NOT_ALLOWED(40405,"方法不被允许"), 18 | EXCEPTION(50000, "业务处理失败,请稍后再试"); 19 | 20 | Integer code; 21 | String msg; 22 | 23 | ResponseStatus(Integer code, String msg) { 24 | this.code = code; 25 | this.msg = msg; 26 | } 27 | 28 | public Integer code() { 29 | return this.code; 30 | } 31 | 32 | public String msg() { 33 | return this.msg; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/convert/ConvertUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.convert; 2 | 3 | import org.modelmapper.ModelMapper; 4 | 5 | import java.net.URLDecoder; 6 | 7 | 8 | /** 9 | * 常用转换工具类 10 | * @author Simon.Xue 11 | * @date 2018.09.28 12 | */ 13 | public class ConvertUtils { 14 | private static ModelMapper modelMapper = new ModelMapper(); 15 | 16 | /** 17 | * 对象转换 18 | * @param object 转换前的对象 19 | * @param tClass 转换后的对象 20 | * @param 21 | * @return 22 | */ 23 | public static D convert(Object object, Class tClass) { 24 | //map是映射处理方法 25 | return modelMapper.map(object, tClass); 26 | } 27 | 28 | public static String getURLDecoder(String arg0) throws Exception { 29 | if (arg0 != null && !"".equals(arg0)) { 30 | arg0 = arg0.trim(); 31 | return URLDecoder.decode(arg0, "UTF-8"); 32 | } 33 | return ""; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/response/PagedResponse.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.response; 2 | 3 | public class PagedResponse extends BaseResponse { 4 | /** 5 | * 当前页数 6 | */ 7 | private int pageIndex; 8 | /** 9 | * 分页条数 10 | */ 11 | private int pageSize; 12 | /** 13 | * 总条数 14 | */ 15 | private long count; 16 | 17 | public PagedResponse() { 18 | } 19 | 20 | public PagedResponse(ResponseStatus responseStatus) { 21 | super(responseStatus); 22 | } 23 | 24 | public int getPageIndex() { 25 | return pageIndex; 26 | } 27 | 28 | public void setPageIndex(int pageIndex) { 29 | this.pageIndex = pageIndex; 30 | } 31 | 32 | public int getPageSize() { 33 | return pageSize; 34 | } 35 | 36 | public void setPageSize(int pageSize) { 37 | this.pageSize = pageSize; 38 | } 39 | 40 | public long getCount() { 41 | return count; 42 | } 43 | 44 | public void setCount(long count) { 45 | this.count = count; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/response/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.response; 2 | 3 | import java.io.Serializable; 4 | 5 | public class BaseResponse implements Serializable { 6 | private Integer code; 7 | private String msg; 8 | private Object data; 9 | 10 | public Integer getCode() { 11 | return code; 12 | } 13 | 14 | public void setCode(Integer code) { 15 | this.code = code; 16 | } 17 | 18 | public String getMsg() { 19 | return msg; 20 | } 21 | 22 | public void setMsg(String msg) { 23 | this.msg = msg; 24 | } 25 | 26 | public Object getData() { 27 | return data; 28 | } 29 | 30 | public void setData(Object data) { 31 | this.data = data; 32 | } 33 | 34 | public void setStatus(ResponseStatus status) { 35 | this.code = status.code(); 36 | this.msg = status.msg(); 37 | } 38 | 39 | public BaseResponse(Integer code, String msg) { 40 | this.code = code; 41 | this.msg = msg; 42 | } 43 | 44 | public BaseResponse(ResponseStatus status) { 45 | this.code = status.code(); 46 | this.msg = status.msg(); 47 | } 48 | 49 | public BaseResponse() { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/encryption/MD5Utils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.encryption; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | /** 7 | * @author wjn 8 | * MD5加密工具类 9 | */ 10 | public class MD5Utils { 11 | 12 | public static String getPassword(String pwd){ 13 | //加密动作,获取加密的对象 14 | try { 15 | MessageDigest digest = MessageDigest.getInstance("md5"); 16 | //对数据进行加密,加密的动作已经完成 17 | byte[] bs = digest.digest(pwd.getBytes()); 18 | 19 | //对加密后的结果,进行优化,将加密后的结果,先转换成正数,然后,转换成16进制格式 20 | String password = ""; 21 | for (byte b : bs) { 22 | //转换成正数 23 | //b类型byte 进行 & 255 int类型数据,自动类型提升 24 | //b:1111 1001 25 | //b转换成int类型之后:0000 0000 0000 0000 0000 0000 1111 1001,最高位变成0之后转换成正数 26 | int temp = b & 255; 27 | //转换成16进制格式 28 | String hexString = Integer.toHexString(temp); 29 | if(temp >=0 && temp < 16){ 30 | password = password +"0"+ hexString; 31 | }else{ 32 | password = password + hexString; 33 | } 34 | } 35 | 36 | return password; 37 | } catch (NoSuchAlgorithmException e) { 38 | e.printStackTrace(); 39 | return ""; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/BeanCompareUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao; 2 | 3 | import org.springframework.beans.BeanWrapper; 4 | import org.springframework.beans.BeanWrapperImpl; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | /** 10 | * @author Simon.Xue 11 | * @date 2019-12-10 21:33 12 | **/ 13 | public class BeanCompareUtils { 14 | /** 15 | * 获得对象的Null或者空字符串的属性名,适用于BeanUtils复制 16 | * @param source 对象源 17 | * @return 18 | */ 19 | public static String[] getEmptyPropertyNames (Object source) { 20 | final BeanWrapper src = new BeanWrapperImpl(source); 21 | java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); 22 | 23 | Set emptyNames = new HashSet(); 24 | for(java.beans.PropertyDescriptor pd : pds) { 25 | Object srcValue = src.getPropertyValue(pd.getName()); 26 | if (srcValue == null) { 27 | emptyNames.add(pd.getName()); 28 | } else if (srcValue instanceof String && ((String) srcValue).isEmpty()) { 29 | emptyNames.add(pd.getName()); 30 | } 31 | 32 | } 33 | String[] result = new String[emptyNames.size()]; 34 | return emptyNames.toArray(result); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/GenerateSequenceUtil.java: -------------------------------------------------------------------------------- 1 | package me.xueyao; 2 | 3 | import java.text.DecimalFormat; 4 | import java.text.FieldPosition; 5 | import java.text.Format; 6 | import java.text.NumberFormat; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Calendar; 9 | 10 | /** 11 | * 根据时间生成唯一序列ID
12 | * 时间精确到秒,ID最大值为99999且循环使用 13 | * 14 | * @Author:chenssy 15 | * @date:2016年4月17日 16 | */ 17 | public class GenerateSequenceUtil { 18 | private static final FieldPosition HELPER_POSITION = new FieldPosition(0); 19 | 20 | /** 时间:精确到秒 */ 21 | private final static Format dateFormat = new SimpleDateFormat("YYYYMMddHHmmss"); 22 | 23 | private final static NumberFormat numberFormat = new DecimalFormat("00000"); 24 | 25 | private static int seq = 0; 26 | 27 | private static final int MAX = 99999; 28 | 29 | public static synchronized String generateSequenceNo() { 30 | 31 | Calendar rightNow = Calendar.getInstance(); 32 | 33 | StringBuffer sb = new StringBuffer(); 34 | 35 | dateFormat.format(rightNow.getTime(), sb, HELPER_POSITION); 36 | 37 | numberFormat.format(seq, sb, HELPER_POSITION); 38 | 39 | if (seq == MAX) { 40 | seq = 0; 41 | } else { 42 | seq++; 43 | } 44 | 45 | return sb.toString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/http/CookieUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.http; 2 | 3 | import javax.servlet.http.Cookie; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | /** 8 | * Cookie工具类 9 | * 10 | */ 11 | public class CookieUtils { 12 | 13 | /** 14 | * 添加cookie 15 | * 16 | * @param response 17 | * @param name 18 | * @param value 19 | * @param maxAge 20 | */ 21 | public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) { 22 | Cookie cookie = new Cookie(name, value); 23 | cookie.setPath("/"); 24 | if (maxAge > 0) { 25 | cookie.setMaxAge(maxAge); 26 | } 27 | response.addCookie(cookie); 28 | } 29 | 30 | /** 31 | * 删除cookie 32 | * 33 | * @param response 34 | * @param name 35 | */ 36 | public static void removeCookie(HttpServletResponse response, String name) { 37 | Cookie uid = new Cookie(name, null); 38 | uid.setPath("/"); 39 | uid.setMaxAge(0); 40 | response.addCookie(uid); 41 | } 42 | 43 | /** 44 | * 获取cookie值 45 | * 46 | * @param request 47 | * @return 48 | */ 49 | public static String getUid(HttpServletRequest request,String cookieName) { 50 | Cookie cookies[] = request.getCookies(); 51 | for (Cookie cookie : cookies) { 52 | if (cookie.getName().equals(cookieName)) { 53 | return cookie.getValue(); 54 | } 55 | } 56 | return null; 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/me/xueyao/http/MailQQUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.http; 2 | 3 | import javax.mail.Message; 4 | import javax.mail.Session; 5 | import javax.mail.Transport; 6 | import javax.mail.internet.InternetAddress; 7 | import javax.mail.internet.MimeMessage; 8 | import java.util.Date; 9 | import java.util.Properties; 10 | 11 | public class MailQQUtils { 12 | /** 13 | * 发送邮件 14 | * @param to 接收邮件的地址 15 | * @param subject 邮件主题 16 | * @param msgText 邮件内容 17 | */ 18 | public static void send(String to, String subject, String msgText) { 19 | //发邮件的地址 20 | String from = "发邮件的地址"; 21 | String password = "授权码"; 22 | //邮件发送服务器地址 23 | String host = "smtp.qq.com"; 24 | //是否开启debug模式 25 | boolean debug = true; 26 | 27 | // 设置发送邮件的配置信息 28 | Properties props = new Properties(); 29 | props.put("mail.smtp.host", host); 30 | 31 | if (debug) { 32 | props.put("mail.debug", debug); 33 | } 34 | //添加auth认证 35 | props.put("mail.smtp.auth", "true"); 36 | props.put("mail.smtp.port", "587"); 37 | 38 | //邮件会话 39 | Session session = Session.getInstance(props, null); 40 | session.setDebug(debug); 41 | 42 | try { 43 | //创建邮件 44 | MimeMessage msg = new MimeMessage(session); 45 | msg.setFrom(new InternetAddress(from)); 46 | InternetAddress[] address = { new InternetAddress(to) }; 47 | msg.setRecipients(Message.RecipientType.TO, address); 48 | //设置主题 49 | msg.setSubject(subject); 50 | //设置发送时间 51 | msg.setSentDate(new Date()); 52 | //设置邮件的内容 53 | msg.setText(msgText); 54 | //发送邮件 55 | Transport.send(msg,from,password); 56 | } catch (Exception mex) { 57 | mex.printStackTrace(); 58 | } 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/tabooed/TabooedUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.tabooed; 2 | 3 | 4 | import me.xueyao.validate.Validators; 5 | 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | 11 | /** 12 | * 对文本内容进行敏感词汇过滤的工具类。 13 | * 14 | * @author xuan 15 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午10:24:01 $ 16 | */ 17 | public abstract class TabooedUtils { 18 | 19 | private static final TabooedTools tabooedTools = TabooedTools.getInstance(); 20 | 21 | /** 22 | * 对文本内容进行过滤,获取所有存在的敏感词汇。 23 | * 24 | * @param content 25 | * 需要进行过滤的内容 26 | * @return 过滤的敏感词汇列表 27 | */ 28 | public static List getTabooedWords(String content) { 29 | if (Validators.isEmpty(content)) { 30 | return Collections.emptyList(); 31 | } 32 | 33 | return tabooedTools.getTabooedWords(content); 34 | } 35 | 36 | /** 37 | * 对文本内容进行检查,验证是否存在敏感词汇。 38 | * 39 | * @param content 40 | * 需要进行检查的内容 41 | * @return 如果存在敏感词汇返回 true,否则返回 false。 42 | */ 43 | public static boolean isTabooed(String content) { 44 | return !getTabooedWords(content).isEmpty(); 45 | } 46 | 47 | /** 48 | * 此方法可以实现在不重启JVM的情况下重新加载存放敏感词汇的资源文件。 49 | */ 50 | public static void reloadTabooedWords() { 51 | tabooedTools.initialize(); 52 | } 53 | 54 | /** 55 | * 此方法用于加载应用程序外部的敏感词汇库,比如数据库里存储的词库(使用该方法后tabooed.words里面的词汇无效) 56 | * 57 | * @param tabooedWords 58 | */ 59 | public static void setTabooedWordsResource(Collection tabooedWords) { 60 | tabooedTools.setTabooedWords(tabooedWords); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/http/HttpIpAddress.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.http; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | public class HttpIpAddress { 6 | /** 7 | * 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址, 8 | * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值 9 | * 10 | * @return ip 11 | */ 12 | public static String getIpAddr(HttpServletRequest request) { 13 | String ip = request.getHeader("x-forwarded-for"); 14 | System.out.println("x-forwarded-for ip: " + ip); 15 | if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { 16 | // 多次反向代理后会有多个ip值,第一个ip才是真实ip 17 | if( ip.indexOf(",")!=-1 ){ 18 | ip = ip.split(",")[0]; 19 | } 20 | } 21 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 22 | ip = request.getHeader("Proxy-Client-IP"); 23 | } 24 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 25 | ip = request.getHeader("WL-Proxy-Client-IP"); 26 | } 27 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 28 | ip = request.getHeader("HTTP_CLIENT_IP"); 29 | } 30 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 31 | ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 32 | } 33 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 34 | ip = request.getHeader("X-Real-IP"); 35 | } 36 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 37 | ip = request.getRemoteAddr(); 38 | } 39 | return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/CompositeComparator.java: -------------------------------------------------------------------------------- 1 | package me.xueyao; 2 | 3 | import java.util.Comparator; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * 用来解决字段的组合排列问题的比较器。 9 | * 10 | *

11 | * 运用了组合(Compositor)模式:把一些具有不同功能的类组合起来成一个类或者数组,
12 | * 然后通过调用某个方法来循环数组中的所有元素,最后返回用户所需要的结果。 13 | * 14 | * @author xuan 15 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午10:18:35 $ 16 | */ 17 | public class CompositeComparator implements Comparator { 18 | 19 | /** 20 | * 比较器列表, 越排在列表前面的比较器优先级越高. 21 | */ 22 | private final List> comparatorList = new LinkedList>(); 23 | 24 | /** 25 | * 获取比较器列表. 26 | */ 27 | public List> getComparatorList() { 28 | return comparatorList; 29 | } 30 | 31 | /** 32 | * 添加一个比较器到比较器列表中. 33 | */ 34 | public void addComparator(Comparator comparator) { 35 | if (comparator == null) { 36 | return; 37 | } 38 | 39 | comparatorList.add(comparator); 40 | } 41 | 42 | /** 43 | * 添加多个比较器到比较器列表中. 44 | */ 45 | public void addComparators(Comparator[] comparators) { 46 | if (comparators == null) { 47 | return; 48 | } 49 | 50 | for (int i = 0; i < comparators.length; i++) { 51 | comparatorList.add(comparators[i]); 52 | } 53 | } 54 | 55 | /** 56 | * 根据比较器列表中的比较器的优先级来对对象进行排序. 优先级高的比较器会先被使用.
57 | * 如果两对象比较结果相等, 则会继续使用其次优先级的比较器进行比较. 58 | */ 59 | @Override 60 | public int compare(T o1, T o2) { 61 | for (Comparator comparator : comparatorList) { 62 | int result = comparator.compare(o1, o2); 63 | if (result != 0) { 64 | return result; 65 | } 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/tabooed/TabooedWords.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.tabooed; 2 | 3 | 4 | import me.xueyao.validate.Validators; 5 | import me.xueyao.file.FileIoUtil; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.util.Collection; 11 | import java.util.HashSet; 12 | 13 | /** 14 | * 敏感词汇类. 此类会读取资源文件(默认为tabooed.words)中的词汇. 15 | * 16 | * @author xuan 17 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午10:24:31 $ 18 | */ 19 | public class TabooedWords { 20 | 21 | private final Collection tabooedWords = new HashSet(); 22 | 23 | /** 24 | * 从默认的敏感词汇文件中读取词汇, 初始化敏感词汇列表. 25 | */ 26 | public synchronized void initialize() { 27 | InputStream in = TabooedWords.class.getClassLoader().getResourceAsStream("tabooed.words"); 28 | initialize(in, "UTF-8"); 29 | } 30 | 31 | /** 32 | * 从指定的敏感词汇输入流中读取词汇, 初始化敏感词汇列表. 33 | * 34 | * @param in 35 | * 敏感词汇输入流 36 | * @param charset 37 | * 编码方式 38 | */ 39 | public synchronized void initialize(InputStream in, String charset) { 40 | 41 | BufferedReader reader = null; 42 | try { 43 | reader = new BufferedReader(new InputStreamReader(in, charset)); 44 | for (String line = reader.readLine(); line != null; line = reader.readLine()) { 45 | if (!Validators.isEmpty(line)) { 46 | tabooedWords.add(line.toLowerCase()); 47 | } 48 | } 49 | } 50 | catch (Exception e) { 51 | // Ignore 52 | } 53 | finally { 54 | FileIoUtil.close(reader); 55 | } 56 | } 57 | 58 | /** 59 | * 获取所有读取到的敏感词汇. 60 | * 61 | * @return 敏感词汇 62 | */ 63 | public Collection getTabooedWords() { 64 | return tabooedWords; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/collection/CloneUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.collection; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | import java.io.Serializable; 9 | import java.util.Collection; 10 | 11 | /** 12 | * 克隆工具类,进行深克隆,包括对象、集合 13 | * 14 | * @Author:chenssy 15 | * @date:2014年8月9日 16 | */ 17 | public class CloneUtils { 18 | 19 | /** 20 | * 采用对象的序列化完成对象的深克隆 21 | * @autor:chenssy 22 | * @date:2014年8月9日 23 | * 24 | * @param obj 25 | * 待克隆的对象 26 | * @return 27 | */ 28 | @SuppressWarnings("unchecked") 29 | public static T cloneObject(T obj) { 30 | T cloneObj = null; 31 | try { 32 | // 写入字节流 33 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 34 | ObjectOutputStream obs = new ObjectOutputStream(out); 35 | obs.writeObject(obj); 36 | obs.close(); 37 | 38 | // 分配内存,写入原始对象,生成新对象 39 | ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); 40 | ObjectInputStream ois = new ObjectInputStream(ios); 41 | // 返回生成的新对象 42 | cloneObj = (T) ois.readObject(); 43 | ois.close(); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } 47 | return cloneObj; 48 | } 49 | 50 | /** 51 | * 利用序列化完成集合的深克隆 52 | * @autor:chenssy 53 | * @date:2014年8月9日 54 | * 55 | * @param collection 56 | * 待克隆的集合 57 | * @return 58 | * @throws ClassNotFoundException 59 | * @throws IOException 60 | */ 61 | @SuppressWarnings("unchecked") 62 | public static Collection cloneCollection(Collection collection) throws ClassNotFoundException, IOException{ 63 | ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); 64 | ObjectOutputStream out = new ObjectOutputStream(byteOut); 65 | out.writeObject(collection); 66 | out.close(); 67 | 68 | ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); 69 | ObjectInputStream in = new ObjectInputStream(byteIn); 70 | Collection dest = (Collection) in.readObject(); 71 | in.close(); 72 | 73 | return dest; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/http/HttpHelper.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.http; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | import java.io.BufferedReader; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | 9 | /** 10 | * HTTP辅助类: 11 | * 12 | * 获取HttpRequest的真实IP 13 | * 获取HttpResponse的Body 14 | * 15 | */ 16 | public class HttpHelper { 17 | 18 | private static final String X_FORWARDED__FOR = "x-forwarded-for"; 19 | private static final String PROXY_CLIENT_IP = "Proxy-Client-IP"; 20 | private static final String WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP"; 21 | private static final String UNKNOWN = "unknown"; 22 | 23 | /** 24 | * 获取登录用户远程主机ip地址 25 | * 26 | * @author 喻聪 27 | * @date 2017-9-29 28 | */ 29 | public static String getIpAddr(HttpServletRequest request) { 30 | String ip = request.getHeader(X_FORWARDED__FOR); 31 | if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 32 | ip = request.getHeader(PROXY_CLIENT_IP); 33 | } 34 | if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 35 | ip = request.getHeader(WL_PROXY_CLIENT_IP); 36 | } 37 | if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { 38 | ip = request.getRemoteAddr(); 39 | } 40 | return ip; 41 | } 42 | 43 | public static void setResponse(HttpServletResponse response, int value, 44 | Object object) { 45 | response.setStatus(value); 46 | PrintWriter out = null; 47 | try { 48 | response.setContentType("text/html;charset=utf-8"); 49 | out = response.getWriter(); 50 | out.println(object); 51 | } catch (IOException e) { 52 | System.out.println("io输出异常:" + e.getMessage()); 53 | } finally { 54 | if(out != null) { 55 | out.flush(); 56 | out.close(); 57 | } 58 | } 59 | } 60 | 61 | 62 | /** 63 | * 获取请求体中的字符串(POST) 64 | */ 65 | public static String getBodyData(BufferedReader reader) { 66 | StringBuffer data = new StringBuffer(); 67 | String line = null; 68 | try { 69 | while (null != (line = reader.readLine())) { 70 | data.append(line); 71 | } 72 | } catch (IOException e) { 73 | e.printStackTrace(); 74 | } finally { 75 | 76 | } 77 | return data.toString(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/format/TimestampUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.format; 2 | 3 | import me.xueyao.date.DateFormatUtils; 4 | 5 | import java.sql.Timestamp; 6 | import java.text.SimpleDateFormat; 7 | import java.util.Date; 8 | 9 | /** 10 | * TimeStamp工具类,提供TimeStamp与String、Date的转换 11 | * 12 | * @author chenssy 13 | * @date 2016-09-24 14 | * @since 1.0.0 15 | */ 16 | public class TimestampUtils { 17 | 18 | /** 19 | * String转换为TimeStamp 20 | * @param value 21 | * 待转换的String,格式必须为 yyyy-mm-dd hh:mm:ss[.f...] 这样的格式,中括号表示可选,否则报错 22 | * @return java.sql.Timestamp 23 | * 24 | * @author chenssy 25 | * @date 2016-09-24 26 | * @since v1.0.0 27 | */ 28 | public static Timestamp string2Timestamp(String value){ 29 | if(value == null && !"".equals(value.trim())){ 30 | return null; 31 | } 32 | Timestamp ts = new Timestamp(System.currentTimeMillis()); 33 | ts = Timestamp.valueOf(value); 34 | return ts; 35 | } 36 | 37 | /** 38 | * 将Timestamp 转换为String类型,format为null则使用默认格式 yyyy-MM-dd HH:mm:ss 39 | * 40 | * @param value 41 | * 待转换的Timestamp 42 | * @param format 43 | * String的格式 44 | * @return java.lang.String 45 | * 46 | * @author chenssy 47 | * @date 2016-09-24 48 | * @since v1.0.0 49 | */ 50 | public static String timestamp2String(Timestamp value,String format){ 51 | if(null == value){ 52 | return ""; 53 | } 54 | SimpleDateFormat sdf = DateFormatUtils.getFormat(format); 55 | 56 | return sdf.format(value); 57 | } 58 | 59 | /** 60 | * Date转换为Timestamp 61 | * 62 | * @param date 63 | * 待转换的Date 64 | * @return java.sql.Timestamp 65 | * 66 | * @author chenssy 67 | * @date 2016-09-24 68 | * @since v1.0.0 69 | */ 70 | public static Timestamp date2Timestamp(Date date){ 71 | if(date == null){ 72 | return null; 73 | } 74 | return new Timestamp(date.getTime()); 75 | } 76 | 77 | /** 78 | * Timestamp转换为Date 79 | * 80 | * @param time 81 | * 待转换的Timestamp 82 | * @return java.util.Date 83 | * 84 | * @author chenssy 85 | * @date 2016-09-24 86 | * @since v1.0.0 87 | */ 88 | public static Date timestamp2Date(Timestamp time){ 89 | return time == null ? null : time; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/response/R.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.response; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | /** 9 | * @author Simon.Xue 10 | * @date 2020-03-03 20:26 11 | **/ 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Getter 15 | @Setter 16 | public class R { 17 | /** 18 | * 请求成功 19 | */ 20 | public static final int CODE_SUCCESS = 200; 21 | 22 | /** 23 | * 系统异常 24 | */ 25 | public static final int CODE_SYS_ERROR = 500; 26 | 27 | /** 28 | * 参数错误 29 | */ 30 | public static final int CODE_PARAMS_ERROR = 400; 31 | 32 | /** 33 | * 超时错误,如:登陆超时、授权超时等; 34 | */ 35 | public static final int CODE_TIME_OUT = 401; 36 | 37 | /** 38 | * 用户不存在错误,必要情况跳转登陆页面; 39 | */ 40 | public static final int CODE_NULL_USER = 402; 41 | 42 | /** 43 | * 用户权限不匹配,无操作权限; 44 | */ 45 | public static final int CODE_AUTHORITY_ERROR = 403; 46 | /** 47 | * 系统异常 提示信息 48 | */ 49 | public static final String MESSAGE_SYS_ERROR = "系统异常"; 50 | 51 | /** 52 | * 操作成功提示信息 53 | */ 54 | public static final String MESSAGE_SUCCESS = "操作成功"; 55 | private Integer code; 56 | private String message; 57 | private T data; 58 | 59 | public R(String msg, T object) { 60 | this.message = msg; 61 | this.data = object; 62 | } 63 | 64 | public R(Integer code, String msg) { 65 | this.code = code; 66 | this.message = msg; 67 | } 68 | 69 | 70 | public T getData() { 71 | return this.data; 72 | } 73 | 74 | 75 | public static R ofParam(String msg) { 76 | return new R(CODE_PARAMS_ERROR, msg); 77 | } 78 | 79 | public static R ofParam(T data, String msg) { 80 | R r = new R(); 81 | r.setCode(CODE_PARAMS_ERROR); 82 | r.setMessage(msg); 83 | r.setData(data); 84 | return r; 85 | } 86 | 87 | public static R ofSuccess(String msg) { 88 | return new R(CODE_SUCCESS, msg); 89 | } 90 | 91 | public static R ofSuccess(String msg, T data) { 92 | return new R(CODE_SUCCESS, msg, data); 93 | } 94 | 95 | public static R ofSystem(String msg) { 96 | return new R(CODE_SYS_ERROR, msg); 97 | } 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/response/ResultPage.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.response; 2 | 3 | import com.github.pagehelper.Page; 4 | import lombok.Data; 5 | import org.springframework.util.CollectionUtils; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | import java.util.function.Function; 10 | import java.util.stream.Collectors; 11 | 12 | /** 13 | * 查询结果分页 14 | * 15 | * @param 16 | */ 17 | @Data 18 | public class ResultPage { 19 | 20 | private int pageIndex = 1; 21 | 22 | private int pageSize = 20; 23 | 24 | private long total; 25 | 26 | private List results; 27 | 28 | public ResultPage convert(Function mapper) { 29 | ResultPage ret = new ResultPage<>(); 30 | ret.setPageIndex(this.getPageIndex()); 31 | ret.setPageSize(this.getPageSize()); 32 | ret.setTotal(this.getTotal()); 33 | if (!CollectionUtils.isEmpty(this.getResults())) { 34 | ret.setResults(this.getResults().stream().map(mapper).filter(o -> o != null).collect(Collectors.toList())); 35 | } 36 | return ret; 37 | } 38 | 39 | public List getResults() { 40 | if (results == null) { 41 | return Collections.emptyList(); 42 | } else { 43 | return results; 44 | } 45 | } 46 | 47 | 48 | public ResultPage() { 49 | } 50 | 51 | public ResultPage(int pageIndex, int pageSize, long total, List results) { 52 | this.pageIndex = pageIndex; 53 | this.pageSize = pageSize; 54 | this.total = total; 55 | this.results = results; 56 | } 57 | 58 | public ResultPage(Page page, List results) { 59 | this.pageIndex = page.getPageNum(); 60 | this.pageSize = page.getPageSize(); 61 | this.total = page.getTotal(); 62 | this.results = results; 63 | } 64 | 65 | /** 66 | * 对list进行手动分页 67 | * @param pageNum 68 | * @param pageSize 69 | * @param list 70 | * @return 71 | */ 72 | public ResultPage customizePage(int pageNum, int pageSize, List list) { 73 | int total = list.isEmpty() ? 0 : list.size(); 74 | int startIndex = (pageNum - 1) * pageSize; 75 | int endIndex = Math.min(startIndex + pageSize, total); 76 | List ts; 77 | 78 | 79 | if (startIndex > total || startIndex < 0) { 80 | ts = Collections.EMPTY_LIST; 81 | } else { 82 | ts = list.subList(startIndex, endIndex); 83 | } 84 | return new ResultPage<>(pageNum, pageSize, total, ts); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/encryption/AESUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.encryption; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.KeyGenerator; 5 | import javax.crypto.spec.SecretKeySpec; 6 | import java.security.SecureRandom; 7 | 8 | 9 | /** 10 | * AES加解密 11 | * 12 | * @Author:chenssy 13 | * @date:2016年5月21日 上午9:01:41 14 | */ 15 | class AESUtils { 16 | /** 17 | * 默认秘钥 18 | */ 19 | protected static final String KEY = "NOPO3nzMD3dndwS0MccuMeXCHgVlGOoYyFwLdS24Im2e7YyhB0wrUsyYf0"; 20 | 21 | /** 22 | * AES解密 23 | * 24 | * @param encryptValue 待解密内容 25 | * @param key 秘钥 26 | * @return 27 | * @throws Exception 28 | * @author:chenssy 29 | * @date : 2016年5月21日 上午9:48:12 30 | */ 31 | protected static String decrypt(String encryptValue, String key) throws Exception { 32 | return aesDecryptByBytes(base64Decode(encryptValue), key); 33 | } 34 | 35 | /** 36 | * AES加密 37 | * 38 | * @param value 待加密内容 39 | * @param key 秘钥 40 | * @return 41 | * @throws Exception 42 | * @author:chenssy 43 | * @date : 2016年5月21日 上午9:48:42 44 | */ 45 | protected static String encrypt(String value, String key) throws Exception { 46 | return base64Encode(aesEncryptToBytes(value, key)); 47 | } 48 | 49 | private static String base64Encode(byte[] bytes) { 50 | return Base64Utils.encrypt(bytes); 51 | } 52 | 53 | @SuppressWarnings("static-access") 54 | private static byte[] base64Decode(String base64Code) throws Exception { 55 | return base64Code == null ? null : Base64Utils.decrypt(base64Code); 56 | } 57 | 58 | private static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { 59 | KeyGenerator kgen = KeyGenerator.getInstance("AES"); 60 | kgen.init(128, new SecureRandom(encryptKey.getBytes())); 61 | 62 | Cipher cipher = Cipher.getInstance("AES"); 63 | cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES")); 64 | 65 | return cipher.doFinal(content.getBytes("utf-8")); 66 | } 67 | 68 | private static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception { 69 | KeyGenerator kgen = KeyGenerator.getInstance("AES"); 70 | kgen.init(128, new SecureRandom(decryptKey.getBytes())); 71 | 72 | Cipher cipher = Cipher.getInstance("AES"); 73 | cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES")); 74 | byte[] decryptBytes = cipher.doFinal(encryptBytes); 75 | 76 | return new String(decryptBytes); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/encryption/DESUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.encryption; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.SecretKey; 5 | import javax.crypto.SecretKeyFactory; 6 | import javax.crypto.spec.DESKeySpec; 7 | import java.security.SecureRandom; 8 | 9 | /** 10 | * DES加解密工具类 11 | * 12 | * @Author:chenssy 13 | * @date:2016年5月20日 下午5:19:00 14 | */ 15 | class DESUtils { 16 | /** 17 | * 默认key 18 | */ 19 | protected final static String KEY = "ScAKC0XhadTHT3Al0QIDAQAB"; 20 | 21 | /** 22 | * DES加密 23 | * 24 | * @param data 待加密字符串 25 | * @param key 校验位 26 | * @return 27 | * @author : chenssy 28 | * @date : 2016年5月20日 下午5:51:37 29 | */ 30 | @SuppressWarnings("restriction") 31 | protected static String encrypt(String data, String key) { 32 | String encryptedData = null; 33 | try { 34 | // DES算法要求有一个可信任的随机数源 35 | SecureRandom sr = new SecureRandom(); 36 | DESKeySpec deskey = new DESKeySpec(key.getBytes()); 37 | // 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象 38 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); 39 | SecretKey secretKey = keyFactory.generateSecret(deskey); 40 | // 加密对象 41 | Cipher cipher = Cipher.getInstance("DES"); 42 | cipher.init(Cipher.ENCRYPT_MODE, secretKey, sr); 43 | // 加密,并把字节数组编码成字符串 44 | encryptedData = new sun.misc.BASE64Encoder().encode(cipher.doFinal(data.getBytes())); 45 | } catch (Exception e) { 46 | throw new RuntimeException("加密错误,错误信息:", e); 47 | } 48 | return encryptedData; 49 | } 50 | 51 | /** 52 | * DES解密 53 | * 54 | * @param cryptData 待解密密文 55 | * @param key 校验位 56 | * @return 57 | * @author : chenssy 58 | * @date : 2016年5月20日 下午5:52:23 59 | */ 60 | @SuppressWarnings("restriction") 61 | protected static String decrypt(String cryptData, String key) { 62 | String decryptedData = null; 63 | try { 64 | // DES算法要求有一个可信任的随机数源 65 | SecureRandom sr = new SecureRandom(); 66 | DESKeySpec deskey = new DESKeySpec(key.getBytes()); 67 | // 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象 68 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); 69 | SecretKey secretKey = keyFactory.generateSecret(deskey); 70 | // 解密对象 71 | Cipher cipher = Cipher.getInstance("DES"); 72 | cipher.init(Cipher.DECRYPT_MODE, secretKey, sr); 73 | // 把字符串解码为字节数组,并解密 74 | decryptedData = new String(cipher.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(cryptData))); 75 | } catch (Exception e) { 76 | throw new RuntimeException("解密错误,错误信息:", e); 77 | } 78 | return decryptedData; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/cache/ConcurrentHashMapUtil.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.cache; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | /** 7 | * ConcurrentHashMap 实现带过期时间的缓存 8 | * 9 | * @author Simon.Xue 10 | * @date 2020-03-26 11:34 11 | **/ 12 | public class ConcurrentHashMapUtil { 13 | /** 14 | * 预缓存信息 15 | */ 16 | private static final Map CACHE_MAP = new ConcurrentHashMap<>(); 17 | 18 | /** 19 | * 每个缓存生效时间2小时 20 | */ 21 | public static final long CACHE_HOLD_TIME_2H = 2 * 60 * 60 * 1000L; 22 | 23 | /** 24 | * 每个缓存生效时间12小时 25 | */ 26 | public static final long CACHE_HOLD_TIME_12H = 12 * 60 * 60 * 1000L; 27 | 28 | /** 29 | * 每个缓存生效时间24小时 30 | */ 31 | public static final long CACHE_HOLD_TIME_24H = 24 * 60 * 60 * 1000L; 32 | 33 | /** 34 | * 存放一个缓存对象,默认保存时间2小时 35 | * 36 | * @param cacheName 37 | * @param obj 38 | */ 39 | public static void put(String cacheName, Object obj) { 40 | put(cacheName, obj, CACHE_HOLD_TIME_2H); 41 | } 42 | 43 | /** 44 | * 存放一个缓存对象,保存时间为holdTime 45 | * 46 | * @param cacheName 47 | * @param obj 48 | * @param holdTime 49 | */ 50 | public static void put(String cacheName, Object obj, long holdTime) { 51 | if (checkCacheName(cacheName)) { 52 | return; 53 | } 54 | CACHE_MAP.put(cacheName, obj); 55 | //缓存失效时间 56 | CACHE_MAP.put(cacheName + "_HoldTime", System.currentTimeMillis() + holdTime); 57 | } 58 | 59 | /** 60 | * 取出一个缓存对象 61 | * 62 | * @param cacheName 63 | * @return 64 | */ 65 | public static Object get(String cacheName) { 66 | if (checkCacheName(cacheName)) { 67 | return CACHE_MAP.get(cacheName); 68 | } 69 | return null; 70 | } 71 | 72 | /** 73 | * 删除所有缓存 74 | */ 75 | public static void removeAll() { 76 | CACHE_MAP.clear(); 77 | } 78 | 79 | /** 80 | * 删除某个缓存 81 | * 82 | * @param cacheName 83 | */ 84 | public static void remove(String cacheName) { 85 | CACHE_MAP.remove(cacheName); 86 | CACHE_MAP.remove(cacheName + "_HoldTime"); 87 | } 88 | 89 | /** 90 | * 检查缓存对象是否存在, 91 | * 若不存在,则返回false 92 | * 若存在,检查其是否已过有效期,如果已经过了则删除该缓存并返回false 93 | * 94 | * @param cacheName 95 | * @return 96 | */ 97 | public static boolean checkCacheName(String cacheName) { 98 | Long cacheHoldTime = (Long) CACHE_MAP.get(cacheName + "_HoldTime"); 99 | if (cacheHoldTime == null || cacheHoldTime == 0L) { 100 | return false; 101 | } 102 | if (cacheHoldTime < System.currentTimeMillis()) { 103 | remove(cacheName); 104 | return false; 105 | } 106 | return true; 107 | } 108 | 109 | } 110 | 111 | 112 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/encryption/MD5.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.encryption; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.math.BigInteger; 6 | import java.nio.charset.Charset; 7 | import java.security.MessageDigest; 8 | import java.security.NoSuchAlgorithmException; 9 | 10 | /** 11 | * md5工具 12 | * 13 | * @author Simon.Xue 14 | * @date 2016-06-10 15 | * 16 | */ 17 | public class MD5 { 18 | 19 | /** 20 | * 计算字符串的MD5值(UTF-8) 21 | * 22 | * @param source 23 | * @return MD5值 24 | */ 25 | public static String getMD5(String source) { 26 | return getMD5(source, null); 27 | } 28 | 29 | /** 30 | * 计算字符串的MD5值(UTF-8) 31 | * 32 | * @param source 33 | * @return MD5值 34 | */ 35 | public static String getMD5(String source, String update) { 36 | return getMD5(source, update, Charset.forName("UTF-8")); 37 | } 38 | 39 | /** 40 | * 计算32位MD5值 41 | */ 42 | public static String getMD5(String source, String update, Charset charset) { 43 | String hashValue = ""; 44 | try { 45 | MessageDigest md = MessageDigest.getInstance("MD5"); 46 | md.update(source.getBytes(charset)); 47 | if (update != null) { 48 | hashValue = byte2hex(md.digest(update.getBytes(charset))); 49 | } else { 50 | hashValue = byte2hex(md.digest()); 51 | } 52 | } catch (NoSuchAlgorithmException e) { 53 | System.err.println("计算MD5值出错:" + e.getMessage()); 54 | } 55 | return hashValue; 56 | } 57 | 58 | /** 59 | * 字节数组转十六进制字符串 60 | */ 61 | public static String byte2hex(byte[] b) { 62 | if (b == null) { 63 | return ""; 64 | } 65 | String hex = ""; 66 | String tmp = ""; 67 | for (int n = 0; n < b.length; n++) { 68 | tmp = (Integer.toHexString(b[n] & 0xff)); 69 | if (tmp.length() == 1) { 70 | hex = hex + "0" + tmp; 71 | } else { 72 | hex = hex + tmp; 73 | } 74 | } 75 | return hex.toLowerCase(); 76 | } 77 | 78 | /** 79 | * 计算文件md5 80 | */ 81 | public static String getFileMD5(String fileStr) { 82 | File file = new File(fileStr); 83 | if (!file.isFile()) { 84 | System.err.println("文件不存在,fileStr:" + fileStr); 85 | return null; 86 | } 87 | MessageDigest digest = null; 88 | FileInputStream in = null; 89 | byte buffer[] = new byte[1024]; 90 | int len; 91 | try { 92 | digest = MessageDigest.getInstance("MD5"); 93 | in = new FileInputStream(file); 94 | while ((len = in.read(buffer, 0, 1024)) != -1) { 95 | digest.update(buffer, 0, len); 96 | } 97 | in.close(); 98 | } catch (Exception e) { 99 | System.err.println("计算md5出错,fileStr:" + fileStr); 100 | return null; 101 | } 102 | BigInteger bigInt = new BigInteger(1, digest.digest()); 103 | return bigInt.toString(16); 104 | } 105 | 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/file/ZipUitls.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.file; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileNotFoundException; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.util.zip.ZipEntry; 10 | import java.util.zip.ZipOutputStream; 11 | 12 | /** 13 | * 文件压缩、解压工具类。文件压缩格式为zip 14 | * 15 | * @Author:chenssy 16 | * @date:2016年5月24日 下午9:16:01 17 | */ 18 | public class ZipUitls { 19 | /** 文件后缀名 */ 20 | private static final String ZIP_FILE_SUFFIX = ".zip"; 21 | 22 | /** 23 | * 压缩文件 24 | * 25 | * @author:chenssy 26 | * @date : 2016年5月24日 下午9:56:36 27 | * 28 | * @param resourcePath 29 | * 源文件 30 | * @param targetPath 31 | * 目的文件,保存文件路径 32 | */ 33 | public static void zipFile(String resourcePath,String targetPath){ 34 | File resourcesFile = new File(resourcePath); 35 | File targetFile = new File(targetPath); 36 | 37 | //目的文件不存在,则新建 38 | if(!targetFile.exists()){ 39 | targetFile.mkdirs(); 40 | } 41 | //文件名 42 | String targetName = resourcesFile.getName() + ZIP_FILE_SUFFIX; 43 | 44 | ZipOutputStream out = null; 45 | try { 46 | FileOutputStream outputStream = new FileOutputStream(targetPath+"//"+targetName); 47 | out = new ZipOutputStream(new BufferedOutputStream(outputStream)); 48 | 49 | compressedFile(out, resourcesFile, ""); 50 | } catch (FileNotFoundException e) { 51 | e.printStackTrace(); 52 | }finally{ 53 | if (out != null) { 54 | try { 55 | out.close(); 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * 65 | * @author:chenssy 66 | * @date : 2016年5月24日 下午10:00:22 67 | * 68 | * @param out 69 | * @param file 70 | * @param dir 71 | */ 72 | private static void compressedFile(ZipOutputStream out, File file, String dir) { 73 | FileInputStream fis = null; 74 | try { 75 | if (file.isDirectory()) { 76 | // 得到文件列表信息 77 | File[] files = file.listFiles(); 78 | // 将文件夹添加到下一级打包目录 79 | out.putNextEntry(new ZipEntry(dir + "/")); 80 | 81 | dir = dir.length() == 0 ? "" : dir + "/"; 82 | 83 | // 循环将文件夹中的文件打包 84 | for (int i = 0; i < files.length; i++) { 85 | // 递归处理 86 | compressedFile(out, files[i], dir + files[i].getName()); 87 | } 88 | } else { //如果是文件则打包处理 89 | fis = new FileInputStream(file); 90 | 91 | out.putNextEntry(new ZipEntry(dir)); 92 | // 进行写操作 93 | int j = 0; 94 | byte[] buffer = new byte[1024]; 95 | while ((j = fis.read(buffer)) > 0) { 96 | out.write(buffer, 0, j); 97 | } 98 | // 关闭输入流 99 | } 100 | } catch (FileNotFoundException e) { 101 | e.printStackTrace(); 102 | } catch (IOException e) { 103 | e.printStackTrace(); 104 | } finally{ 105 | if(fis != null){ 106 | try { 107 | fis.close(); 108 | } catch (IOException e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/convert/BeanMapConvert.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.convert; 2 | 3 | import java.beans.BeanInfo; 4 | import java.beans.IntrospectionException; 5 | import java.beans.Introspector; 6 | import java.beans.PropertyDescriptor; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * Bean与Map的转换 14 | * 15 | * @author chenssy 16 | * @date 2016-09-24 17 | * @since 1.0.0 18 | */ 19 | public class BeanMapConvert { 20 | /** 21 | * Bean转换为Map 22 | * 23 | * @param object 24 | * @return String-Object的HashMap 25 | * 26 | * @author chenssy 27 | * @date 2016-09-25 28 | * @since v1.0.0 29 | */ 30 | public static Map bean2MapObject(Object object){ 31 | if(object == null){ 32 | return null; 33 | } 34 | 35 | Map map = new HashMap(); 36 | try { 37 | BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass()); 38 | PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); 39 | for (PropertyDescriptor property : propertyDescriptors) { 40 | String key = property.getName(); 41 | // 过滤class属性 42 | if (!key.equals("class")) { 43 | // 得到property对应的getter方法 44 | Method getter = property.getReadMethod(); 45 | Object value = getter.invoke(object); 46 | 47 | map.put(key, value); 48 | } 49 | } 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | 54 | return map; 55 | } 56 | 57 | /** 58 | * Map转换为Java Bean 59 | * 60 | * @param map 61 | * 待转换的Map 62 | * @param object 63 | * Java Bean 64 | * @return java.lang.Object 65 | * 66 | * @author chenssy 67 | * @date 2016-09-25 68 | * @since v1.0.0 69 | */ 70 | public static Object map2Bean(Map map,Object object){ 71 | if(map == null || object == null){ 72 | return null; 73 | } 74 | try { 75 | BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass()); 76 | PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); 77 | 78 | for (PropertyDescriptor property : propertyDescriptors) { 79 | String key = property.getName(); 80 | if (map.containsKey(key)) { 81 | Object value = map.get(key); 82 | // 得到property对应的setter方法 83 | Method setter = property.getWriteMethod(); 84 | setter.invoke(object, value); 85 | } 86 | } 87 | } catch (IntrospectionException e) { 88 | e.printStackTrace(); 89 | } catch (InvocationTargetException e) { 90 | e.printStackTrace(); 91 | } catch (IllegalAccessException e) { 92 | e.printStackTrace(); 93 | } 94 | return object; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/SimonTreeUtil.java: -------------------------------------------------------------------------------- 1 | package me.xueyao; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | /** 10 | * 树形结构工具 11 | * @author Simon.Xue 12 | * @date 2/23/21 9:57 PM 13 | **/ 14 | public class SimonTreeUtil { 15 | 16 | /** 17 | * 所有子部门的id列表 18 | */ 19 | private static List childDeptId = new ArrayList<>(); 20 | 21 | /** 22 | * 递归获得 父id下的所有id 23 | * @param departmentList 所有部门列表 24 | * @param pid 指定的PID 25 | * @return 26 | */ 27 | public static List treeDepartIdList(List departmentList, String pid) { 28 | for (Department dept : departmentList) { 29 | //遍历出父id等于参数的id,add进子节点集合 30 | if (dept.getPid().equals(pid)) { 31 | //递归遍历下一级 32 | treeDepartIdList(departmentList, String.valueOf(dept.getId())); 33 | childDeptId.add(String.valueOf(dept.getId())); 34 | } 35 | } 36 | return childDeptId; 37 | } 38 | 39 | /** 40 | * 递归获得 所有上级部门列表(非树型结构) 41 | * 不包含自身列表,请注意去重 42 | * @date 2021-03-22 19:35:25 43 | * @param departmentAllList 所有部门列表 44 | * @param departmentChildList 子部门列表(搜索匹配到的部门列表) 45 | * @param result 最后符合的部门列表 46 | */ 47 | public static void childParentList(List departmentAllList, 48 | List departmentChildList, 49 | List result) { 50 | 51 | for (Department child : departmentChildList) { 52 | if (child.getPid().equals("0")) { 53 | result.add(child); 54 | continue; 55 | } 56 | 57 | Department depart = departmentAllList.stream() 58 | .filter(department -> child.getPid().equals(department.getId())) 59 | .findFirst() 60 | .get(); 61 | 62 | result.add(depart); 63 | 64 | if (!depart.getPid().equals("0")) { 65 | childParentList(departmentAllList, Arrays.asList(depart), result); 66 | } 67 | } 68 | 69 | } 70 | 71 | /** 72 | * 递归所有部门(树型结构) 73 | * 不建议使用该方法,请使用Hutool工具包中的TreeUtil,简单方便 74 | * @param code 主部门code 75 | * @param departmentList 全部部门列表 76 | * @return 树型结构列表 77 | */ 78 | public static List getChild(String code, List departmentList) { 79 | ArrayList childDept = new ArrayList<>(); 80 | for (Department department : departmentList) { 81 | if (department.getPid().equals(code)) { 82 | childDept.add(department); 83 | } 84 | } 85 | 86 | for (Department child : childDept) { 87 | child.setDepartmentList(getChild(child.getId(), departmentList)); 88 | } 89 | 90 | 91 | if (0 == childDept.size()) { 92 | return null; 93 | } 94 | return childDept; 95 | } 96 | 97 | @Data 98 | class Department { 99 | private String id; 100 | private String pid; 101 | 102 | List departmentList; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/cache/LRUPlusCache.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.cache; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.LinkedList; 6 | import java.util.Map; 7 | 8 | /** 9 | * 缓存实现,采用LRU算法,能够取得被挤出缓存的对象
10 | * LRU是Least Recently Used的缩写,即最近最少使用页面置换算法 11 | * 12 | * @author xuan 13 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午10:42:57 $ 14 | */ 15 | public class LRUPlusCache implements Serializable { 16 | 17 | private static final long serialVersionUID = -4174256121921240092L; 18 | 19 | private final int capacity; 20 | private final LinkedList list = new LinkedList(); 21 | private final HashMap map = new HashMap(); 22 | 23 | /** 24 | * 构造方法 25 | * 26 | * @param capacity 27 | * 缓存的容量 28 | */ 29 | public LRUPlusCache(int capacity) { 30 | this.capacity = capacity; 31 | } 32 | 33 | /** 34 | * 放到缓存中,会放在缓存中的第一个位置 35 | * 36 | * @param key 37 | * 键 38 | * @param value 39 | * 值 40 | * @return 被挤出的对象,如果为null,说明没有对象被挤出 41 | */ 42 | public synchronized Object putInCache(String key, Object value) { 43 | Object oldValue = map.put(key, value); 44 | if (oldValue != null) { 45 | return null; 46 | } 47 | 48 | list.addFirst(key); 49 | 50 | if (list.size() <= capacity) { 51 | return null; 52 | } 53 | 54 | String removedKey = list.removeLast(); 55 | return map.remove(removedKey); 56 | } 57 | 58 | /** 59 | * 从缓存中读取,被读取的对象会放到缓存中的第一个位置 60 | * 61 | * @param key 62 | * @return 对象 63 | */ 64 | public synchronized Object getFromCache(String key) { 65 | Object value = map.get(key); 66 | if (value != null) { 67 | list.remove(key); 68 | list.addFirst(key); 69 | } 70 | return value; 71 | } 72 | 73 | /** 74 | * 删除缓存中所有对象 75 | * 76 | * @return 被删除的所有对象Map 77 | */ 78 | public synchronized Map removeAll() { 79 | HashMap removedMap = new HashMap(map); 80 | list.clear(); 81 | map.clear(); 82 | return removedMap; 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | StringBuilder StringBuilder = new StringBuilder(); 88 | for (int i = 0; i < list.size(); i++) { 89 | String key = list.get(i); 90 | if (i > 0) { 91 | StringBuilder.append(","); 92 | } 93 | StringBuilder.append(key); 94 | StringBuilder.append("["); 95 | StringBuilder.append(map.get(key)); 96 | StringBuilder.append("]"); 97 | } 98 | return StringBuilder.toString(); 99 | } 100 | 101 | public static void main(String[] args) { 102 | LRUPlusCache cache = new LRUPlusCache(5); 103 | for (int i = 0; i < 6; i++) { 104 | System.out.println(cache.putInCache(i + "", i + "$")); 105 | System.out.println(cache); 106 | } 107 | System.out.println(cache.putInCache("1", "1$")); 108 | System.out.println(cache); 109 | System.out.println(cache.getFromCache("1")); 110 | System.out.println(cache); 111 | System.out.println(cache.getFromCache("4")); 112 | System.out.println(cache); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/format/MathUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.format; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * 由于 Java 的简单类型不能够精确的对浮点数进行运算,此工具类提供精确的浮点数运算,包括加减乘除和四舍五入 7 | * 8 | * @author xuan 9 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午9:53:36 $ 10 | */ 11 | public abstract class MathUtils { 12 | 13 | /** 14 | * 默认除法运算精度 15 | */ 16 | private static final int DEFAULT_DIV_SCALE = 10; 17 | 18 | /** 19 | * 提供精确的加法运算。 20 | * 21 | * @param v1 被加数 22 | * @param v2 加数 23 | * @return 两个参数的和 24 | */ 25 | public static double add(double v1, double v2) { 26 | BigDecimal b1 = createBigDecimal(v1); 27 | BigDecimal b2 = createBigDecimal(v2); 28 | 29 | return b1.add(b2).doubleValue(); 30 | } 31 | 32 | /** 33 | * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到小数点以后10位,以后的数字四舍五入。 34 | * 35 | * @param v1 被除数 36 | * @param v2 除数 37 | * @return 两个参数的商 38 | */ 39 | public static double div(double v1, double v2) { 40 | return div(v1, v2, DEFAULT_DIV_SCALE); 41 | } 42 | 43 | /** 44 | * 提供(相对)精确的除法运算。 当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。 45 | * 46 | * @param v1 被除数 47 | * @param v2 除数 48 | * @param scale 表示表示需要精确到小数点以后几位。 49 | * @return 两个参数的商 50 | */ 51 | public static double div(double v1, double v2, int scale) { 52 | if (scale < 0) { 53 | throw new IllegalArgumentException("The scale must be a positive integer or zero"); 54 | } 55 | 56 | BigDecimal b1 = createBigDecimal(v1); 57 | BigDecimal b2 = createBigDecimal(v2); 58 | 59 | return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 60 | } 61 | 62 | /** 63 | * 判断 double 值是否非法,值为 Infinite 或者 NaN 即表示非法。 64 | * 65 | * @param v doube 值 66 | * @return 如果值为 Infinite 或者 NaN 则返回 true,否则返回 false。 67 | */ 68 | public static boolean isInvalidDouble(double v) { 69 | return Double.isInfinite(v) || Double.isNaN(v); 70 | } 71 | 72 | /** 73 | * 提供精确的乘法运算。 74 | * 75 | * @param v1 被乘数 76 | * @param v2 乘数 77 | * @return 两个参数的积 78 | */ 79 | public static double mul(double v1, double v2) { 80 | BigDecimal b1 = createBigDecimal(v1); 81 | BigDecimal b2 = createBigDecimal(v2); 82 | 83 | return b1.multiply(b2).doubleValue(); 84 | } 85 | 86 | /** 87 | * 提供精确的小数位四舍五入处理。如果 v 是非法的,则原样返回。 88 | * 89 | * @param v 需要四舍五入的数字 90 | * @param scale 小数点后保留几位 91 | * @return 四舍五入后的结果 92 | */ 93 | public static double round(double v, int scale) { 94 | if (scale < 0) { 95 | throw new IllegalArgumentException("The scale must be a positive integer or zero"); 96 | } 97 | 98 | if (isInvalidDouble(v)) { 99 | return v; 100 | } 101 | 102 | BigDecimal b = createBigDecimal(v); 103 | return b.divide(BigDecimal.ONE, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 104 | } 105 | 106 | /** 107 | * 提供精确的减法运算。 108 | * 109 | * @param v1 被减数 110 | * @param v2 减数 111 | * @return 两个参数的差 112 | */ 113 | public static double sub(double v1, double v2) { 114 | BigDecimal b1 = createBigDecimal(v1); 115 | BigDecimal b2 = createBigDecimal(v2); 116 | 117 | return b1.subtract(b2).doubleValue(); 118 | } 119 | 120 | /** 121 | * 采用 BigDecimal 的字符串构造器进行初始化。 122 | * 123 | * @param v double 值 124 | * @return BigDecimal 对象 125 | */ 126 | private static BigDecimal createBigDecimal(double v) { 127 | return new BigDecimal(Double.toString(v)); 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/encryption/Base64Utils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.encryption; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | /** 6 | * BASE64加解密工具类 7 | * 8 | * @Author:chenssy 9 | * @date:2016年5月20日 下午5:05:30 10 | * 11 | */ 12 | class Base64Utils { 13 | 14 | private static char[] base64EncodeChars = new char[] { 'A', 'B', 'C', 'D', 15 | 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 16 | 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 17 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 18 | 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', 19 | '4', '5', '6', '7', '8', '9', '+', '/' }; 20 | 21 | private static byte[] base64DecodeChars = new byte[] { -1, -1, -1, -1, -1, 22 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24 | -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 25 | 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 26 | 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, 27 | -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 28 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 29 | -1, -1 }; 30 | 31 | /** 32 | * BASE64加密 33 | * 34 | * @author : chenssy 35 | * @date : 2016年5月20日 下午5:10:18 36 | * 37 | * @param data 38 | * @return 39 | */ 40 | protected static String encrypt(byte[] data) { 41 | StringBuffer sb = new StringBuffer(); 42 | int len = data.length; 43 | int i = 0; 44 | int b1, b2, b3; 45 | while (i < len) { 46 | b1 = data[i++] & 0xff; 47 | if (i == len) { 48 | sb.append(base64EncodeChars[b1 >>> 2]); 49 | sb.append(base64EncodeChars[(b1 & 0x3) << 4]); 50 | sb.append("=="); 51 | break; 52 | } 53 | b2 = data[i++] & 0xff; 54 | if (i == len) { 55 | sb.append(base64EncodeChars[b1 >>> 2]); 56 | sb.append(base64EncodeChars[((b1 & 0x03) << 4) 57 | | ((b2 & 0xf0) >>> 4)]); 58 | sb.append(base64EncodeChars[(b2 & 0x0f) << 2]); 59 | sb.append("="); 60 | break; 61 | } 62 | b3 = data[i++] & 0xff; 63 | sb.append(base64EncodeChars[b1 >>> 2]); 64 | sb.append(base64EncodeChars[((b1 & 0x03) << 4) 65 | | ((b2 & 0xf0) >>> 4)]); 66 | sb.append(base64EncodeChars[((b2 & 0x0f) << 2) 67 | | ((b3 & 0xc0) >>> 6)]); 68 | sb.append(base64EncodeChars[b3 & 0x3f]); 69 | } 70 | return sb.toString(); 71 | } 72 | 73 | /** 74 | * Base64 解密 75 | * 76 | * @author : chenssy 77 | * @date : 2016年5月20日 下午5:11:51 78 | * 79 | * @param str 80 | * @return 81 | * @throws UnsupportedEncodingException 82 | */ 83 | protected static byte[] decrypt(String str) throws Exception{ 84 | StringBuffer sb = new StringBuffer(); 85 | byte[] data = str.getBytes("US-ASCII"); 86 | int len = data.length; 87 | int i = 0; 88 | int b1, b2, b3, b4; 89 | while (i < len) { 90 | 91 | do { 92 | b1 = base64DecodeChars[data[i++]]; 93 | } while (i < len && b1 == -1); 94 | if (b1 == -1) { 95 | break; 96 | } 97 | 98 | do { 99 | b2 = base64DecodeChars[data[i++]]; 100 | } while (i < len && b2 == -1); 101 | if (b2 == -1) { 102 | break; 103 | } 104 | sb.append((char) ((b1 << 2) | ((b2 & 0x30) >>> 4))); 105 | 106 | do { 107 | b3 = data[i++]; 108 | if (b3 == 61) 109 | return sb.toString().getBytes("iso8859-1"); 110 | b3 = base64DecodeChars[b3]; 111 | } while (i < len && b3 == -1); 112 | if (b3 == -1) 113 | break; 114 | sb.append((char) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2))); 115 | 116 | do { 117 | b4 = data[i++]; 118 | if (b4 == 61) 119 | return sb.toString().getBytes("iso8859-1"); 120 | b4 = base64DecodeChars[b4]; 121 | } while (i < len && b4 == -1); 122 | if (b4 == -1) 123 | break; 124 | sb.append((char) (((b3 & 0x03) << 6) | b4)); 125 | } 126 | return sb.toString().getBytes("iso8859-1"); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/validate/ValidateHelper.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.validate; 2 | 3 | import java.lang.reflect.Array; 4 | import java.util.Collection; 5 | import java.util.Date; 6 | import java.util.Map; 7 | 8 | /** 9 | * 判断对象、字符串、集合是否为空、不为空 10 | * 11 | * @Author:chenssy 12 | * @date:2014年8月4日 13 | */ 14 | public final class ValidateHelper { 15 | 16 | /** 17 | * 判断数组是否为空 18 | * @author chenssy 19 | * @date Dec 23, 2013 20 | * @param array 21 | * @return boolean 22 | */ 23 | @SuppressWarnings("unused") 24 | private static boolean isEmptyArray(T[] array){ 25 | if (array == null || array.length == 0){ 26 | return true; 27 | } 28 | else{ 29 | return false; 30 | } 31 | } 32 | 33 | /** 34 | * 判断数组是否不为空 35 | * @author chenssy 36 | * @date Dec 23, 2013 37 | * @param array 38 | * @return boolean 39 | */ 40 | public static boolean isNotEmptyArray(T[] array){ 41 | if (array != null && array.length > 0){ 42 | return true; 43 | } 44 | else{ 45 | return false; 46 | } 47 | } 48 | 49 | /** 50 | * 判断字符串是否为空 51 | * @author chenssy 52 | * @date Dec 23, 2013 53 | * @param string 54 | * @return boolean 55 | */ 56 | public static boolean isEmptyString(String string){ 57 | if (string == null || string.length() == 0){ 58 | return true; 59 | } 60 | else{ 61 | return false; 62 | } 63 | } 64 | 65 | /** 66 | * 判断字符串是否不为空 67 | * @author chenssy 68 | * @date Dec 23, 2013 69 | * @param string 70 | * @return boolean 71 | */ 72 | public static boolean isNotEmptyString(String string){ 73 | if (string != null && string.length() > 0){ 74 | return true; 75 | } 76 | else{ 77 | return false; 78 | } 79 | } 80 | 81 | /** 82 | * 判断集合是否为空 83 | * @author chenssy 84 | * @date Dec 26, 2013 85 | * @param collection 86 | * @return boolean 87 | */ 88 | public static boolean isEmptyCollection(Collection collection){ 89 | if (collection == null || collection.isEmpty()){ 90 | return true; 91 | } 92 | else{ 93 | return false; 94 | } 95 | } 96 | 97 | /** 98 | * 判断集合是否不为空 99 | * @author chenssy 100 | * @date Dec 26, 2013 101 | * @param collection 102 | * @return boolean 103 | */ 104 | public static boolean isNotEmptyCollection(Collection collection){ 105 | if (collection != null && !collection.isEmpty()){ 106 | return true; 107 | } 108 | else{ 109 | return false; 110 | } 111 | } 112 | 113 | /** 114 | * 判断map集合是否不为空 115 | * @author chenssy 116 | * @date Dec 26, 2013 117 | * @param map 118 | * @return boolean 119 | */ 120 | @SuppressWarnings("rawtypes") 121 | public static boolean isNotEmptyMap(Map map){ 122 | if (map != null && !map.isEmpty()){ 123 | return true; 124 | } 125 | else{ 126 | return false; 127 | } 128 | } 129 | 130 | /** 131 | * 判断map集合是否为空 132 | * @author ming.chen 133 | * @date Dec 26, 2013 134 | * @param map 135 | * @return boolean 136 | */ 137 | @SuppressWarnings("rawtypes") 138 | public static boolean isEmptyMap(Map map){ 139 | if (map == null || map.isEmpty()){ 140 | return true; 141 | } 142 | else{ 143 | return false; 144 | } 145 | } 146 | 147 | /** 148 | * 检验对象是否为空,String 中只有空格在对象中也算空. 149 | * @param object 150 | * @return 为空返回true,否则false. 151 | */ 152 | @SuppressWarnings("rawtypes") 153 | public static boolean isEmpty(Object object) { 154 | if (null == object) { 155 | return true; 156 | } else if (object instanceof String) { 157 | return "".equals(object.toString().trim()); 158 | } else if (object instanceof Iterable) { 159 | return !((Iterable) object).iterator().hasNext(); 160 | } else if (object.getClass().isArray()) { 161 | return Array.getLength(object) == 0; 162 | } else if (object instanceof Map) { 163 | return ((Map) object).size() == 0; 164 | } else if (Number.class.isAssignableFrom(object.getClass())) { 165 | return false; 166 | } else if (Date.class.isAssignableFrom(object.getClass())) { 167 | return false; 168 | } else{} 169 | 170 | return false; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.2.RELEASE 9 | 10 | 11 | me.xueyao 12 | simon-java-utils 13 | 0.0.1-SNAPSHOT 14 | Awesome-Java-Utils 15 | Java Utils 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | 28 | org.projectlombok 29 | lombok 30 | true 31 | 32 | 33 | 34 | 35 | org.apache.lucene 36 | lucene-core 37 | 8.3.1 38 | 39 | 40 | 41 | 42 | org.apache.lucene 43 | lucene-analyzers-common 44 | 8.3.1 45 | 46 | 47 | 48 | 49 | javax.mail 50 | javax.mail-api 51 | 1.6.2 52 | 53 | 54 | 55 | 56 | org.modelmapper 57 | modelmapper 58 | 2.3.5 59 | 60 | 61 | 62 | 63 | net.coobird 64 | thumbnailator 65 | [0.4, 0.5) 66 | 67 | 68 | 69 | org.apache.httpcomponents 70 | httpclient 71 | 4.5.3 72 | 73 | 74 | 75 | com.alibaba 76 | fastjson 77 | 1.2.72 78 | 79 | 80 | 81 | org.springframework.boot 82 | spring-boot-starter-test 83 | test 84 | 85 | 86 | org.junit.vintage 87 | junit-vintage-engine 88 | 89 | 90 | 91 | 92 | 93 | 94 | com.github.pagehelper 95 | pagehelper-spring-boot-starter 96 | 1.2.3 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-maven-plugin 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/tabooed/SensitiveWordInit.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.tabooed; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | import java.util.Iterator; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | /** 14 | * 初始化敏感词库,将敏感词加入到HashMap中,构建DFA算法模型 15 | * 16 | * @Author : chenssy 17 | * @Date : 2014年4月20日 下午2:27:06 18 | */ 19 | public class SensitiveWordInit { 20 | //字符编码 21 | private String ENCODING = "GBK"; 22 | @SuppressWarnings("rawtypes") 23 | public HashMap sensitiveWordMap; 24 | 25 | SensitiveWordInit(){ 26 | super(); 27 | } 28 | 29 | /** 30 | * @author chenssy 31 | * @date 2014年4月20日 下午2:28:32 32 | * @version 1.0 33 | */ 34 | @SuppressWarnings("rawtypes") 35 | Map initKeyWord(){ 36 | try { 37 | //读取敏感词库 38 | Set keyWordSet = readSensitiveWordFile(); 39 | //将敏感词库加入到HashMap中 40 | addSensitiveWordToHashMap(keyWordSet); 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | return sensitiveWordMap; 45 | } 46 | 47 | /** 48 | * 读取敏感词库,将敏感词放入HashSet中,构建一个DFA算法模型:
49 | * 中 = { 50 | * isEnd = 0 51 | * 国 = {
52 | * isEnd = 1 53 | * 人 = {isEnd = 0 54 | * 民 = {isEnd = 1} 55 | * } 56 | * 男 = { 57 | * isEnd = 0 58 | * 人 = { 59 | * isEnd = 1 60 | * } 61 | * } 62 | * } 63 | * } 64 | * 五 = { 65 | * isEnd = 0 66 | * 星 = { 67 | * isEnd = 0 68 | * 红 = { 69 | * isEnd = 0 70 | * 旗 = { 71 | * isEnd = 1 72 | * } 73 | * } 74 | * } 75 | * } 76 | * @author chenssy 77 | * @date 2014年4月20日 下午3:04:20 78 | * @param keyWordSet 敏感词库 79 | * @version 1.0 80 | */ 81 | @SuppressWarnings({ "rawtypes", "unchecked" }) 82 | private void addSensitiveWordToHashMap(Set keyWordSet) { 83 | //初始化敏感词容器,减少扩容操作 84 | sensitiveWordMap = new HashMap(keyWordSet.size()); 85 | String key = null; 86 | Map nowMap = null; 87 | Map newWorMap = null; 88 | //迭代keyWordSet 89 | Iterator iterator = keyWordSet.iterator(); 90 | while(iterator.hasNext()){ 91 | //关键字 92 | key = iterator.next(); 93 | nowMap = sensitiveWordMap; 94 | for(int i = 0 ; i < key.length() ; i++){ 95 | //转换成char型 96 | char keyChar = key.charAt(i); 97 | //获取 98 | Object wordMap = nowMap.get(keyChar); 99 | //如果存在该key,直接赋值 100 | if(wordMap != null){ 101 | nowMap = (Map) wordMap; 102 | } 103 | else{ 104 | //不存在则,则构建一个map,同时将isEnd设置为0,因为他不是最后一个 105 | newWorMap = new HashMap<>(16); 106 | //不是最后一个 107 | newWorMap.put("isEnd", "0"); 108 | nowMap.put(keyChar, newWorMap); 109 | nowMap = newWorMap; 110 | } 111 | 112 | if(i == key.length() - 1){ 113 | //最后一个 114 | nowMap.put("isEnd", "1"); 115 | } 116 | } 117 | } 118 | } 119 | 120 | /** 121 | * 读取敏感词库中的内容,将内容添加到set集合中 122 | * @author chenssy 123 | * @date 2014年4月20日 下午2:31:18 124 | * @return 125 | * @version 1.0 126 | * @throws Exception 127 | */ 128 | @SuppressWarnings("resource") 129 | private Set readSensitiveWordFile() throws Exception{ 130 | Set set = null; 131 | //读取文件 132 | File file = new File("D:\\SensitiveWord.txt"); 133 | InputStreamReader read = new InputStreamReader(new FileInputStream(file),ENCODING); 134 | try { 135 | //文件流是否存在 136 | if(file.isFile() && file.exists()){ 137 | set = new HashSet(); 138 | BufferedReader bufferedReader = new BufferedReader(read); 139 | String txt = null; 140 | //读取文件,将文件内容放入到set中 141 | while((txt = bufferedReader.readLine()) != null){ 142 | set.add(txt); 143 | } 144 | } 145 | else{ //不存在抛出异常信息 146 | throw new Exception("敏感词库文件不存在"); 147 | } 148 | } catch (Exception e) { 149 | throw e; 150 | }finally{ 151 | read.close(); //关闭文件流 152 | } 153 | return set; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/file/FileUtilsSIMON.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.file; 2 | 3 | import java.io.File; 4 | import java.io.FileFilter; 5 | import java.util.Map; 6 | 7 | /** 8 | * @description 文件操作工具类 9 | * @author Yao Xue 10 | * @date Jul 31, 2017 5:13:28 PM 11 | */ 12 | public class FileUtilsSIMON { 13 | /** 14 | * 获得文件夹的大小 15 | * @param dir 文件对象 16 | * @return 文件夹的大小(单位:字节) 17 | */ 18 | public static long folderSize(File dir){ 19 | long length = 0; 20 | // 获得文件数组 21 | File files[] = dir.listFiles(); 22 | // 遍历数组 23 | for (File file : files) { 24 | // 判断是否是文件夹 25 | if(file.isDirectory()) { 26 | length += folderSize(file); 27 | } else { 28 | length += file.length(); 29 | } 30 | } 31 | return length; 32 | } 33 | 34 | /** 35 | * 过滤器筛选将指定文件夹下的小于size的小文件获取并打印 36 | * @param f 文件对象 37 | * @param size 要过滤文件的大小(单位:KB) 38 | */ 39 | public static void filterFile(File f , final Integer size){ 40 | if(f.isFile()){ 41 | System.out.println(f + "不是文件夹"); 42 | return; 43 | } 44 | if(!f.exists()) return; 45 | // 获得文件数组 46 | File files[] = f.listFiles(new FileFilter() { 47 | public boolean accept(File pathname) { 48 | // 判断是否是文件夹,如果是则返回true 49 | if(pathname.isDirectory()) return true; 50 | // 判断是否是隐藏文件 51 | if(pathname.isHidden()) return false; 52 | // 获得文件大小 53 | long length = pathname.length(); 54 | if(length / 1024 < size) return true; 55 | return false; 56 | } 57 | }); 58 | 59 | // 遍历数组 60 | for (File file : files) { 61 | if(file.isDirectory()) { 62 | filterFile(file,size); 63 | continue; 64 | } 65 | System.out.println(file); 66 | } 67 | } 68 | 69 | /** 70 | * 删除某个文件下的所有文件(包括子文件夹) 71 | * @param dir 文件对象 72 | */ 73 | public static void delete(File dir) { 74 | // 获取所有文件 75 | File[] files = dir.listFiles(); 76 | // 遍历子目录,删除子目录 77 | for (File file : files) { 78 | if (file.isFile()) { 79 | boolean success = file.delete(); 80 | // 判断是否删除成功,主要用于测试自己写的代码 81 | if (success) { 82 | System.out.println(file + "删除成功"); 83 | } else { 84 | System.out.println(file + "正在使用,删除失败"); 85 | } 86 | } else { 87 | // 如果是文件夹递归删除 88 | delete(file); 89 | } 90 | } 91 | 92 | // 来到这里说明,该文件夹为空了. 93 | boolean success = dir.delete(); 94 | // 判断是否删除成功,主要用于测试自己写的代码 95 | if (success) { 96 | System.out.println(dir + "删除成功"); 97 | } else { 98 | System.out.println(dir + "正在使用,删除失败"); 99 | } 100 | } 101 | 102 | /** 103 | * 获取文件类型及其个数 104 | * @param f 文件对象 105 | * @param map map集合,存扩展名及个数 106 | */ 107 | public static void getFileType(File f,Map map) { 108 | if(f.isDirectory()) { 109 | File files[] = f.listFiles(); 110 | for (File file : files) { 111 | getFileType(file,map); 112 | } 113 | } else { 114 | // 获得文件名 115 | String fileName = f.getName(); 116 | // 获得文件扩展名 117 | String key = fileName.substring(fileName.lastIndexOf(".") + 1); 118 | // 判断map是否包含key 119 | if(map.containsKey(key)){ 120 | map.put(key, map.get(key) + 1); 121 | } else { 122 | map.put(key, 1); 123 | } 124 | } 125 | } 126 | 127 | /** 128 | * 打印目录结构 129 | * @param file 文件对象 130 | * @param level 目录的层次 131 | */ 132 | public static void printDir(File file, int level) { 133 | for (int i = 0; i < level; i++) { 134 | System.out.print("\t"); 135 | } 136 | System.out.println(file.getName()); 137 | if(file.isDirectory()) { 138 | File[] files = file.listFiles(); 139 | for (File f : files) { 140 | printDir(f,level + 1); 141 | } 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/RandomUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 生成随机数字、字符串的工具类 7 | * 8 | * @author xuan 9 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午10:10:13 $ 10 | */ 11 | public abstract class RandomUtils { 12 | 13 | /** 14 | * 没有添加 I、O 的原因是避免和数字 1、0 混淆 15 | */ 16 | private static final String ALPHA_NUMERIC = "ABCDEFGHJKLMNPQRSTUVWXYZ123456789"; 17 | 18 | private static final String ALL_CHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 19 | private static final String LETTER_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 20 | private static final String NUMBER_CHAR = "0123456789"; 21 | /** 22 | * 产生一个固定范围(min-max 之间)的随机正整数。 23 | * 24 | * @param min 25 | * 最小值, 26 | * @param max 27 | * 最大值 28 | * @return min-max 之间的随机整数,包括 min、max 29 | */ 30 | public static int getRandomInt(int min, int max) { 31 | return (int) (Math.random() * (max - min + 1) + min); 32 | } 33 | 34 | /** 35 | * 产生固定长度的随机数字串。 36 | * 37 | * @param length 38 | * 长度 39 | * @return 随机数字串 40 | */ 41 | public static String getRandomNum(int length) { 42 | return (Double.toString(Math.random())).substring(2, (2 + length)); 43 | } 44 | 45 | /** 46 | * 产生固定长度的随机字母数字串,其中字母为大写方式。 47 | * 48 | * @param length 49 | * 长度 50 | * @return 随机字母数字串 51 | */ 52 | public static String getRandomStr(int length) { 53 | char[] randomBytes = new char[length]; 54 | for (int i = 0; i < length; i++) { 55 | randomBytes[i] = ALPHA_NUMERIC.charAt(getRandomInt(0, ALPHA_NUMERIC.length() - 1)); 56 | } 57 | return new String(randomBytes); 58 | } 59 | 60 | /** 61 | * 产生固定长度的随机字母数字串,其中字母为小写方式。 62 | * 63 | * @param length 64 | * 长度 65 | * @return 随机字母数字串 66 | */ 67 | public static String getRandomStrLowerCase(int length) { 68 | return getRandomStr(length).toLowerCase(); 69 | } 70 | 71 | 72 | /** 73 | * 获取定长的随机数,包含大小写、数字 74 | * @autor:chenssy 75 | * @date:2014年8月11日 76 | * 77 | * @param length 78 | * 随机数长度 79 | * @return 80 | */ 81 | public static String generateString(int length) { 82 | StringBuffer sb = new StringBuffer(); 83 | Random random = new Random(); 84 | for (int i = 0; i < length; i++) { 85 | sb.append(ALL_CHAR.charAt(random.nextInt(ALL_CHAR.length()))); 86 | } 87 | return sb.toString(); 88 | } 89 | 90 | /** 91 | * 获取定长的随机数,包含大小写字母 92 | * @autor:chenssy 93 | * @date:2014年8月11日 94 | * 95 | * @param length 96 | * 随机数长度 97 | * @return 98 | */ 99 | public static String generateMixString(int length) { 100 | StringBuffer sb = new StringBuffer(); 101 | Random random = new Random(); 102 | for (int i = 0; i < length; i++) { 103 | sb.append(LETTER_CHAR.charAt(random.nextInt(LETTER_CHAR.length()))); 104 | } 105 | return sb.toString(); 106 | } 107 | 108 | /** 109 | * 获取定长的随机数,只包含小写字母 110 | * @autor:chenssy 111 | * @date:2014年8月11日 112 | * 113 | * @param length 114 | * 随机数长度 115 | * @return 116 | */ 117 | public static String generateLowerString(int length) { 118 | return generateMixString(length).toLowerCase(); 119 | } 120 | 121 | /** 122 | * 获取定长的随机数,只包含大写字母 123 | * @autor:chenssy 124 | * @date:2014年8月11日 125 | * 126 | * @param length 127 | * 随机数长度 128 | * @return 129 | */ 130 | public static String generateUpperString(int length) { 131 | return generateMixString(length).toUpperCase(); 132 | } 133 | 134 | /** 135 | * 获取定长的随机数,只包含数字 136 | * @autor:chenssy 137 | * @date:2014年8月11日 138 | * 139 | * @param length 140 | * 随机数长度 141 | * @return 142 | */ 143 | public static String generateNumberString(int length){ 144 | StringBuffer sb = new StringBuffer(); 145 | Random random = new Random(); 146 | for (int i = 0; i < length; i++) { 147 | sb.append(NUMBER_CHAR.charAt(random.nextInt(NUMBER_CHAR.length()))); 148 | } 149 | return sb.toString(); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/http/HttpRequestUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.http; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.http.client.config.RequestConfig; 5 | import org.apache.http.client.methods.CloseableHttpResponse; 6 | import org.apache.http.client.methods.HttpGet; 7 | import org.apache.http.client.methods.HttpPost; 8 | import org.apache.http.entity.StringEntity; 9 | import org.apache.http.impl.client.CloseableHttpClient; 10 | import org.apache.http.impl.client.HttpClients; 11 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.io.BufferedReader; 15 | import java.io.IOException; 16 | import java.io.InputStreamReader; 17 | import java.nio.charset.Charset; 18 | 19 | /** 20 | * @Author: Rocky.Jiang 21 | * @Date: Created in 10:26 2018/09/05 22 | */ 23 | @Slf4j 24 | public class HttpRequestUtils { 25 | private static CloseableHttpClient httpClient; 26 | 27 | static { 28 | PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); 29 | cm.setMaxTotal(100); 30 | cm.setDefaultMaxPerRoute(20); 31 | cm.setDefaultMaxPerRoute(50); 32 | httpClient = HttpClients.custom().setConnectionManager(cm).build(); 33 | } 34 | 35 | 36 | public static String get(String url) { 37 | CloseableHttpResponse response = null; 38 | BufferedReader in = null; 39 | String result = ""; 40 | try { 41 | HttpGet httpGet = new HttpGet(url); 42 | RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build(); 43 | httpGet.setConfig(requestConfig); 44 | httpGet.setConfig(requestConfig); 45 | httpGet.addHeader("Content-type", "application/json; charset=utf-8"); 46 | httpGet.setHeader("Accept", "application/json"); 47 | response = httpClient.execute(httpGet); 48 | in = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); 49 | StringBuffer sb = new StringBuffer(""); 50 | String line = ""; 51 | String NL = System.getProperty("line.separator"); 52 | while ((line = in.readLine()) != null) { 53 | sb.append(line + NL); 54 | } 55 | in.close(); 56 | result = sb.toString(); 57 | } catch (IOException e) { 58 | e.printStackTrace(); 59 | } finally { 60 | try { 61 | if (null != response) { 62 | response.close(); 63 | } 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } 67 | } 68 | return result; 69 | } 70 | 71 | public static String post(String url, String jsonString) { 72 | CloseableHttpResponse response = null; 73 | BufferedReader in = null; 74 | String result = ""; 75 | try { 76 | HttpPost httpPost = new HttpPost(url); 77 | RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build(); 78 | httpPost.setConfig(requestConfig); 79 | httpPost.setConfig(requestConfig); 80 | httpPost.addHeader("Content-type", "application/json; charset=utf-8"); 81 | httpPost.setHeader("Accept", "application/json"); 82 | httpPost.setEntity(new StringEntity(jsonString, Charset.forName("UTF-8"))); 83 | response = httpClient.execute(httpPost); 84 | in = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); 85 | StringBuffer sb = new StringBuffer(""); 86 | String line = ""; 87 | String NL = System.getProperty("line.separator"); 88 | while ((line = in.readLine()) != null) { 89 | sb.append(line + NL); 90 | } 91 | in.close(); 92 | result = sb.toString(); 93 | } catch (IOException e) { 94 | e.printStackTrace(); 95 | } finally { 96 | try { 97 | if (null != response) { 98 | response.close(); 99 | } 100 | } catch (IOException e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | return result; 105 | } 106 | 107 | public static String getIpAddr(HttpServletRequest request) { 108 | String ip = request.getHeader("php-x-forwarded-for"); 109 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 110 | ip = request.getHeader("x-forwarded-for"); 111 | } 112 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 113 | ip = request.getHeader("Proxy-Client-IP"); 114 | } 115 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 116 | ip = request.getHeader("WL-Proxy-Client-IP"); 117 | } 118 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 119 | ip = request.getRemoteAddr(); 120 | } 121 | return ip; 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/tabooed/SensitivewordFilterUtil.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.tabooed; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | /** 9 | * 铭感词过滤工具类 10 | * 11 | * @Author:chenssy 12 | * @date:2014年8月5日 13 | */ 14 | public class SensitivewordFilterUtil { 15 | @SuppressWarnings("rawtypes") 16 | private Map sensitiveWordMap = null; 17 | //最小匹配规则 18 | public static int minMatchTYpe = 1; 19 | //最大匹配规则 20 | public static int maxMatchType = 2; 21 | 22 | /** 23 | * 构造函数,初始化敏感词库 24 | */ 25 | public SensitivewordFilterUtil() { 26 | sensitiveWordMap = new SensitiveWordInit().initKeyWord(); 27 | } 28 | 29 | /** 30 | * 判断文字是否包含敏感字符 31 | * 32 | * @param txt 文字 33 | * @param matchType 匹配规则 1:最小匹配规则,2:最大匹配规则 34 | * @return 若包含返回true,否则返回false 35 | * @author chenssy 36 | * @date 2014年4月20日 下午4:28:30 37 | * @version 1.0 38 | */ 39 | public boolean isContaintSensitiveWord(String txt, int matchType) { 40 | boolean flag = false; 41 | for (int i = 0; i < txt.length(); i++) { 42 | //判断是否包含敏感字符 43 | int matchFlag = this.CheckSensitiveWord(txt, i, matchType); 44 | //大于0存在,返回true 45 | if (matchFlag > 0) { 46 | flag = true; 47 | } 48 | } 49 | return flag; 50 | } 51 | 52 | /** 53 | * 获取文字中的敏感词 54 | * 55 | * @param txt 文字 56 | * @param matchType 匹配规则 1:最小匹配规则,2:最大匹配规则 57 | * @return 58 | * @author chenssy 59 | * @date 2014年4月20日 下午5:10:52 60 | * @version 1.0 61 | */ 62 | public Set getSensitiveWord(String txt, int matchType) { 63 | Set sensitiveWordList = new HashSet(); 64 | 65 | for (int i = 0; i < txt.length(); i++) { 66 | //判断是否包含敏感字符 67 | int length = CheckSensitiveWord(txt, i, matchType); 68 | //存在,加入list中 69 | if (length > 0) { 70 | sensitiveWordList.add(txt.substring(i, i + length)); 71 | //减1的原因,是因为for会自增 72 | i = i + length - 1; 73 | } 74 | } 75 | 76 | return sensitiveWordList; 77 | } 78 | 79 | /** 80 | * 替换敏感字字符 81 | * 82 | * @param txt 83 | * @param matchType 84 | * @param replaceChar 替换字符,默认* 85 | * @author chenssy 86 | * @date 2014年4月20日 下午5:12:07 87 | * @version 1.0 88 | */ 89 | public String replaceSensitiveWord(String txt, int matchType, String replaceChar) { 90 | String resultTxt = txt; 91 | //获取所有的敏感词 92 | Set set = getSensitiveWord(txt, matchType); 93 | Iterator iterator = set.iterator(); 94 | String word = null; 95 | String replaceString = null; 96 | while (iterator.hasNext()) { 97 | word = iterator.next(); 98 | replaceString = getReplaceChars(replaceChar, word.length()); 99 | resultTxt = resultTxt.replaceAll(word, replaceString); 100 | } 101 | 102 | return resultTxt; 103 | } 104 | 105 | /** 106 | * 获取替换字符串 107 | * 108 | * @param replaceChar 109 | * @param length 110 | * @return 111 | * @author chenssy 112 | * @date 2014年4月20日 下午5:21:19 113 | * @version 1.0 114 | */ 115 | private String getReplaceChars(String replaceChar, int length) { 116 | String resultReplace = replaceChar; 117 | for (int i = 1; i < length; i++) { 118 | resultReplace += replaceChar; 119 | } 120 | 121 | return resultReplace; 122 | } 123 | 124 | /** 125 | * 检查文字中是否包含敏感字符,检查规则如下:
126 | * 127 | * @param txt 128 | * @param beginIndex 129 | * @param matchType 130 | * @author chenssy 131 | * @date 2014年4月20日 下午4:31:03 132 | * @return,如果存在,则返回敏感词字符的长度,不存在返回0 133 | * @version 1.0 134 | */ 135 | @SuppressWarnings({"rawtypes"}) 136 | public int CheckSensitiveWord(String txt, int beginIndex, int matchType) { 137 | //敏感词结束标识位:用于敏感词只有1位的情况 138 | boolean flag = false; 139 | //匹配标识数默认为0 140 | int matchFlag = 0; 141 | char word = 0; 142 | Map nowMap = sensitiveWordMap; 143 | for (int i = beginIndex; i < txt.length(); i++) { 144 | word = txt.charAt(i); 145 | //获取指定key 146 | nowMap = (Map) nowMap.get(word); 147 | //存在,则判断是否为最后一个 148 | if (nowMap != null) { 149 | //找到相应key,匹配标识+1 150 | matchFlag++; 151 | //如果为最后一个匹配规则,结束循环,返回匹配标识数 152 | if ("1".equals(nowMap.get("isEnd"))) { 153 | //结束标志位为true 154 | flag = true; 155 | //最小规则,直接返回,最大规则还需继续查找 156 | if (SensitivewordFilterUtil.minMatchTYpe == matchType) { 157 | break; 158 | } 159 | } 160 | } else { //不存在,直接返回 161 | break; 162 | } 163 | } 164 | //长度必须大于等于1,为词 165 | if (matchFlag < 2 || !flag) { 166 | matchFlag = 0; 167 | } 168 | return matchFlag; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/collection/BigDecimalUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.collection; 2 | 3 | import me.xueyao.validate.ValidateHelper; 4 | 5 | import java.math.BigDecimal; 6 | 7 | /** 8 | * 提供精确的加减乘除运算 9 | * 10 | * @Author:chenssy 11 | * @date:2014年9月15日 12 | */ 13 | public class BigDecimalUtils { 14 | 15 | /** 16 | * 默认保留位:2 17 | */ 18 | private static int DEFAULT_SCALE = 2; 19 | 20 | /** 21 | * 默认四舍五入规则为:向上舍入 22 | */ 23 | private static int DEFAULT_ROUND = BigDecimal.ROUND_HALF_UP; 24 | 25 | /** 26 | * 27 | * 加法运算 28 | * @autor:chenssy 29 | * @date:2014年9月15日 30 | * 31 | * @param v1 加数 32 | * @param v2 被加数 33 | * @return 34 | */ 35 | public static String add(String v1,String v2){ 36 | BigDecimal b1 = new BigDecimal(v1); 37 | BigDecimal b2 = new BigDecimal(v2); 38 | return b1.add(b2).toString(); 39 | } 40 | 41 | /** 42 | * 除法运算
43 | * 当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。 44 | * @autor:chenssy 45 | * @date:2014年9月15日 46 | * 47 | * @param v1 48 | * 除数 49 | * @param v2 50 | * 被除数 51 | * @param scale 52 | * 精确精度 53 | * @return 54 | */ 55 | public static String div(String v1, String v2, int scale, int round) { 56 | if (scale < 0) { 57 | throw new IllegalArgumentException( 58 | "The scale must be a positive integer or zero"); 59 | } 60 | 61 | if (ValidateHelper.isEmpty(scale)) { 62 | scale = DEFAULT_SCALE; 63 | } 64 | 65 | if (ValidateHelper.isEmpty(round)) { 66 | round = DEFAULT_ROUND; 67 | } 68 | 69 | BigDecimal b1 = new BigDecimal(v1); 70 | BigDecimal b2 = new BigDecimal(v2); 71 | return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString(); 72 | } 73 | 74 | /** 75 | * 比较两个数
76 | * v1 > v2 return 1
77 | * v1 = v2 return 0
78 | * v1 < v2 return -1 79 | * @autor:chenssy 80 | * @date:2014年9月15日 81 | * 82 | * @param v1 83 | * 比较数 84 | * @param v2 85 | * 被比较数 86 | * @return 87 | */ 88 | public static int compareTo(String v1,String v2){ 89 | BigDecimal b1 = new BigDecimal(v1); 90 | BigDecimal b2 = new BigDecimal(v2); 91 | return b1.compareTo(b2); 92 | } 93 | 94 | /** 95 | * 返回较小数 96 | * @autor:chenssy 97 | * @date:2014年9月15日 98 | * 99 | * @param v1 100 | * @param v2 101 | * @return 102 | */ 103 | public static String returnMin(String v1,String v2){ 104 | BigDecimal b1 = new BigDecimal(v1); 105 | BigDecimal b2 = new BigDecimal(v2); 106 | return b1.min(b2).toString(); 107 | } 108 | 109 | /** 110 | * 返回较大数 111 | * @autor:chenssy 112 | * @date:2014年9月15日 113 | * 114 | * @param v1 115 | * @param v2 116 | * @return 117 | */ 118 | public static String returnMax(String v1,String v2){ 119 | BigDecimal b1 = new BigDecimal(v1); 120 | BigDecimal b2 = new BigDecimal(v2); 121 | return b1.max(b2).toString(); 122 | } 123 | 124 | /** 125 | * 处理BigDecimal数据,保留scale位小数 126 | * @author:chenssy 127 | * @date:2014年10月21日 128 | * 129 | * @param value 130 | * @param scale 131 | * @return 132 | */ 133 | public static BigDecimal getValue(BigDecimal value,int scale){ 134 | if(!ValidateHelper.isEmpty(value)){ 135 | return value.setScale(scale, BigDecimal.ROUND_HALF_UP); 136 | } 137 | return value; 138 | } 139 | 140 | /** 141 | * 将object转换为Bigdecimal 142 | * 143 | * @author:chenssy 144 | * @date:2014年10月17日 145 | * 146 | * @param value 147 | * 待转换的数值 148 | * @return 149 | */ 150 | public static BigDecimal getBigDecimal(Object value){ 151 | BigDecimal resultValue = new BigDecimal(0); 152 | if(value instanceof String){ 153 | resultValue = new BigDecimal((String)value); 154 | } 155 | else if(value instanceof Integer){ 156 | resultValue = new BigDecimal((Integer)value); 157 | } 158 | else if(value instanceof Long){ 159 | resultValue = new BigDecimal((Long)value); 160 | } 161 | else if(value instanceof Double){ 162 | resultValue = new BigDecimal((Double)value); 163 | } 164 | else{ 165 | resultValue = (BigDecimal) value; 166 | } 167 | 168 | return resultValue; 169 | } 170 | 171 | 172 | /** 173 | * 将object转换为Bigdecimal,若object为空,则返回resultValue 174 | * 175 | * @autor:chenssy 176 | * @date:2014年9月20日 177 | * 178 | * @param value 179 | * @return 180 | */ 181 | public static BigDecimal getBigDecimal(Object value,BigDecimal resultValue){ 182 | if(ValidateHelper.isEmpty(value)){ 183 | return resultValue; 184 | } 185 | 186 | resultValue = getBigDecimal(resultValue); 187 | 188 | return resultValue; 189 | } 190 | 191 | /** 192 | * 将BigDecimal 转换成Long 193 | * @autor:chenssy 194 | * @date:2014年9月20日 195 | * 196 | * @param value 197 | * @return 198 | */ 199 | public static Long bigDecimalToLong(BigDecimal value){ 200 | if(value != null){ 201 | return new Long(value.longValue()); 202 | } 203 | return null; 204 | } 205 | 206 | /** 207 | * 将BigDecimal 转换成integer 208 | * @autor:huangc 209 | * @date:2014年9月20日 210 | * 211 | * @param value 212 | * @return 213 | */ 214 | public static Integer bigDecimalToInteger(BigDecimal value){ 215 | if(value != null){ 216 | return new Integer(value.intValue()); 217 | } 218 | return null; 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/date/DateFormatUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.date; 2 | 3 | import java.text.ParsePosition; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | 7 | /** 8 | * 日期格式化工具类 9 | * 10 | * @Author:chenssy 11 | * @date:2016年5月26日 下午12:39:57 12 | * 13 | */ 14 | public class DateFormatUtils { 15 | /** yyyy:年 */ 16 | public static final String DATE_YEAR = "yyyy"; 17 | 18 | /** MM:月 */ 19 | public static final String DATE_MONTH = "MM"; 20 | 21 | /** DD:日 */ 22 | public static final String DATE_DAY = "dd"; 23 | 24 | /** HH:时 */ 25 | public static final String DATE_HOUR = "HH"; 26 | 27 | /** mm:分 */ 28 | public static final String DATE_MINUTE = "mm"; 29 | 30 | /** ss:秒 */ 31 | public static final String DATE_SECONDES = "ss"; 32 | 33 | /** yyyy-MM-dd */ 34 | public static final String DATE_FORMAT1 = "yyyy-MM-dd"; 35 | 36 | /** yyyy-MM-dd hh:mm:ss */ 37 | public static final String DATE_FORMAT2 = "yyyy-MM-dd HH:mm:ss"; 38 | 39 | /** yyyy-MM-dd hh:mm:ss|SSS */ 40 | public static final String TIME_FORMAT_SSS = "yyyy-MM-dd HH:mm:ss|SSS"; 41 | 42 | /** yyyyMMdd */ 43 | public static final String DATE_NOFUll_FORMAT = "yyyyMMdd"; 44 | 45 | /** yyyyMMddhhmmss */ 46 | public static final String TIME_NOFUll_FORMAT = "yyyyMMddHHmmss"; 47 | 48 | /** 49 | * 50 | * 格式转换
51 | * yyyy-MM-dd hh:mm:ss 和 yyyyMMddhhmmss 相互转换
52 | * yyyy-mm-dd 和yyyymmss 相互转换 53 | * @author chenssy 54 | * @date Dec 26, 2013 55 | * @param value 56 | * 日期 57 | * @return String 58 | */ 59 | public static String formatString(String value) { 60 | String sReturn = ""; 61 | if (value == null || "".equals(value)) { 62 | return sReturn; 63 | } 64 | //长度为14格式转换成yyyy-mm-dd hh:mm:ss 65 | if (value.length() == 14) { 66 | sReturn = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8) + " " 67 | + value.substring(8, 10) + ":" + value.substring(10, 12) + ":" + value.substring(12, 14); 68 | return sReturn; 69 | } 70 | //长度为19格式转换成yyyymmddhhmmss 71 | if (value.length() == 19) { 72 | sReturn = value.substring(0, 4) + value.substring(5, 7) + value.substring(8, 10) + value.substring(11, 13) 73 | + value.substring(14, 16) + value.substring(17, 19); 74 | return sReturn; 75 | } 76 | //长度为10格式转换成yyyymmhh 77 | if(value.length() == 10){ 78 | sReturn = value.substring(0, 4) + value.substring(5,7) + value.substring(8,10); 79 | } 80 | //长度为8格式转化成yyyy-mm-dd 81 | if(value.length() == 8){ 82 | sReturn = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8); 83 | } 84 | return sReturn; 85 | } 86 | 87 | public static String formatDate(String date, String format) { 88 | if (date == null || "".equals(date)){ 89 | return ""; 90 | } 91 | Date dt = null; 92 | SimpleDateFormat inFmt = null; 93 | SimpleDateFormat outFmt = null; 94 | ParsePosition pos = new ParsePosition(0); 95 | date = date.replace("-", "").replace(":", ""); 96 | if ((date == null) || ("".equals(date.trim()))) { 97 | return ""; 98 | } 99 | try { 100 | if (Long.parseLong(date) == 0L) { 101 | return ""; 102 | } 103 | } catch (Exception nume) { 104 | return date; 105 | } 106 | try { 107 | switch (date.trim().length()) { 108 | case 14: 109 | inFmt = new SimpleDateFormat("yyyyMMddHHmmss"); 110 | break; 111 | case 12: 112 | inFmt = new SimpleDateFormat("yyyyMMddHHmm"); 113 | break; 114 | case 10: 115 | inFmt = new SimpleDateFormat("yyyyMMddHH"); 116 | break; 117 | case 8: 118 | inFmt = new SimpleDateFormat("yyyyMMdd"); 119 | break; 120 | case 6: 121 | inFmt = new SimpleDateFormat("yyyyMM"); 122 | break; 123 | case 7: 124 | case 9: 125 | case 11: 126 | case 13: 127 | default: 128 | return date; 129 | } 130 | if ((dt = inFmt.parse(date, pos)) == null) { 131 | return date; 132 | } 133 | if ((format == null) || ("".equals(format.trim()))) { 134 | outFmt = new SimpleDateFormat("yyyy年MM月dd日"); 135 | } else { 136 | outFmt = new SimpleDateFormat(format); 137 | } 138 | return outFmt.format(dt); 139 | } catch (Exception ex) { 140 | } 141 | return date; 142 | } 143 | 144 | /** 145 | * 格式化日期 146 | * 147 | * @author chenming 148 | * @date 2016年5月31日 149 | * 150 | * @param date 151 | * @param format 152 | * @return 153 | */ 154 | public static String formatDate(Date date,String format){ 155 | return formatDate(DateTools.date2String(date), format); 156 | } 157 | 158 | /** 159 | * @desc:格式化是时间,采用默认格式(yyyy-MM-dd HH:mm:ss) 160 | * @autor:chenssy 161 | * @date:2014年8月6日 162 | * 163 | * @param value 164 | * @return 165 | */ 166 | public static String formatDate(String value){ 167 | return getFormat(DATE_FORMAT2).format(DateTools.string2Date(value, DATE_FORMAT2)); 168 | } 169 | 170 | /** 171 | * 格式化日期 172 | * 173 | * @author : chenssy 174 | * @date : 2016年5月31日 下午5:40:58 175 | * 176 | * @param value 177 | * @return 178 | */ 179 | public static String formatDate(Date value){ 180 | return formatDate(DateTools.date2String(value)); 181 | } 182 | 183 | /** 184 | * 获取日期显示格式,为空默认为yyyy-mm-dd HH:mm:ss 185 | * @author chenssy 186 | * @date Dec 30, 2013 187 | * @param format 188 | * @return 189 | * @return SimpleDateFormat 190 | */ 191 | public static SimpleDateFormat getFormat(String format){ 192 | if(format == null || "".equals(format)){ 193 | format = DATE_FORMAT2; 194 | } 195 | return new SimpleDateFormat(format); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/collection/ArrayUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.collection; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | /** 8 | * 数组工具类 9 | * 10 | * @author xuan 11 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午9:56:54 $ 12 | */ 13 | public abstract class ArrayUtils { 14 | 15 | /** 16 | * 显示字符串数组的内容,用,分隔 17 | * 18 | * @param args 字符串数组 19 | * @return 字符串数组的内容 20 | */ 21 | public static String toString(String[] args) { 22 | return toString(args, ","); 23 | } 24 | 25 | /** 26 | * 显示字符串数组的内容 27 | * 28 | * @param args 字符串数组 29 | * @param separator 分隔符 30 | * @return 字符串数组的内容 31 | */ 32 | public static String toString(String[] args, String separator) { 33 | if (args == null || args.length == 0) { 34 | return null; 35 | } 36 | StringBuilder buffer = new StringBuilder(); 37 | for (int i = 0; i < args.length; i++) { 38 | if (i > 0) { 39 | buffer.append(separator); 40 | } 41 | buffer.append(args[i]); 42 | } 43 | return buffer.toString(); 44 | } 45 | 46 | 47 | /** 48 | * 取得字符串数组的第一个元素 49 | * 50 | * @param stringArray 字符串数组 51 | * @return 字符串数组的第一个元素 52 | */ 53 | public static String getFirst(String[] stringArray) { 54 | if (stringArray == null || stringArray.length == 0) { 55 | return null; 56 | } 57 | return stringArray[0]; 58 | } 59 | 60 | /** 61 | * 取得数组的第一个元素 62 | * 63 | * @param array 数组 64 | * @return 数组的第一个元素 65 | */ 66 | public static Object getFirst(Object[] array) { 67 | if (array == null || array.length == 0) { 68 | return null; 69 | } 70 | return array[0]; 71 | } 72 | 73 | /** 74 | * 把List转换成字符串数组 75 | * 76 | * @param list 字符串List 77 | * @return 字符串数组 78 | */ 79 | public static String[] toArray(List list) { 80 | return list.toArray(new String[list.size()]); 81 | } 82 | 83 | /** 84 | * 把Set转换成字符串数组 85 | * 86 | * @param set 字符串Set 87 | * @return 字符串数组 88 | */ 89 | public static String[] toArray(Set set) { 90 | return set.toArray(new String[set.size()]); 91 | } 92 | 93 | /** 94 | * 判断字符串数组是否包含指定的字符串 95 | * 96 | * @param array 字符串数组 97 | * @param str 指定的字符串 98 | * @return 包含true,否则false 99 | */ 100 | public static boolean contains(String[] array, String str) { 101 | if (array == null || array.length == 0) { 102 | return false; 103 | } 104 | 105 | for (int i = 0; i < array.length; i++) { 106 | if (array[i] == null && str == null) { 107 | return true; 108 | } 109 | if (array[i].equals(str)) { 110 | return true; 111 | } 112 | } 113 | return false; 114 | } 115 | 116 | /** 117 | * 判断字符串数组是否有不为Empty的值 118 | * 119 | * @param args 字符串数组 120 | * @return 有true,否则false 121 | */ 122 | public static boolean hasValue(String[] args) { 123 | if (args == null || args.length == 0 || (args.length == 1 && args[0] == null)) { 124 | return false; 125 | } 126 | for (int i = 0, length = args.length; i < length; i++) { 127 | if (args[i] != null || args[i].trim().length() > 0) { 128 | return true; 129 | } 130 | } 131 | return false; 132 | } 133 | 134 | /** 135 | * 联合两个数组 136 | * 137 | * @param first 第一个数组 138 | * @param last 另一个数组 139 | * @return 内容合并后的数组 140 | */ 141 | public static Object[] combine(Object[] first, Object[] last) { 142 | if (first.length == 0 && last.length == 0) { 143 | return null; 144 | } 145 | Object[] result = new Object[first.length + last.length]; 146 | System.arraycopy(first, 0, result, 0, first.length); 147 | System.arraycopy(last, 0, result, first.length, last.length); 148 | return result; 149 | } 150 | 151 | /** 152 | * 把数组转换成 列表,如果数组为 null,则会返回一个空列表。 153 | * 154 | * @param array 数组 155 | * @return 列表对象 156 | */ 157 | public static List toList(Object[] array) { 158 | ArrayList list = new ArrayList(); 159 | if (array == null) { 160 | return list; 161 | } 162 | 163 | for (int i = 0; i < array.length; i++) { 164 | list.add(array[i]); 165 | } 166 | return list; 167 | } 168 | 169 | /** 170 | * 清除字符串数组中的null 171 | * 172 | * @param array 字符串数组 173 | * @return 清除null后的字符串数组 174 | */ 175 | public static String[] clearNull(String[] array) { 176 | ArrayList list = new ArrayList(); 177 | for (int i = 0; i < array.length; i++) { 178 | if (array[i] != null) { 179 | list.add(array[i]); 180 | } 181 | } 182 | return toArray(list); 183 | } 184 | 185 | /** 186 | * @description 反转任意类型数组 187 | * @author XueYao 188 | * @param arr 任意类型的数组 189 | */ 190 | public static void reverse(E[] arr) { 191 | for (int start = 0, end = arr.length-1; start < end; start++, end--) { 192 | E temp = arr[start]; 193 | arr[start] = arr[end]; 194 | arr[end] = temp; 195 | } 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/IdWorker.java: -------------------------------------------------------------------------------- 1 | package me.xueyao; 2 | 3 | import java.lang.management.ManagementFactory; 4 | import java.net.InetAddress; 5 | import java.net.NetworkInterface; 6 | 7 | /** 8 | * 雪花算法代码实现 9 | * @author Simon.Xue 10 | * @date 1/22/21 5:43 PM 11 | **/ 12 | 13 | public class IdWorker { 14 | /** 15 | * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) 16 | */ 17 | private final static long twEpoch = 1288834974657L; 18 | /** 19 | * 机器标识位数 20 | */ 21 | private final static long workerIdBits = 5L; 22 | /** 23 | * 数据中心标识位数 24 | */ 25 | private final static long dataCenterIdBits = 5L; 26 | /** 27 | * 机器ID最大值 28 | */ 29 | private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); 30 | /** 31 | * 数据中心ID最大值 32 | */ 33 | private final static long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits); 34 | /** 35 | * 毫秒内自增位 36 | */ 37 | private final static long sequenceBits = 12L; 38 | /** 39 | * 机器ID偏左移12位 40 | */ 41 | private final static long workerIdShift = sequenceBits; 42 | /** 43 | * 数据中心ID左移17位 44 | */ 45 | private final static long dataCenterIdShift = sequenceBits + workerIdBits; 46 | /** 47 | * 时间毫秒左移22位 48 | */ 49 | private final static long timestampLeftShift = sequenceBits + workerIdBits + 50 | dataCenterIdBits; 51 | 52 | private final static long sequenceMask = -1L ^ (-1L << sequenceBits); 53 | /** 54 | * 上次生产id时间戳 55 | */ 56 | private static long lastTimestamp = -1L; 57 | /** 58 | * 0,并发控制 59 | */ 60 | private long sequence = 0L; 61 | private final long workerId; 62 | /** 63 | * 数据标识id部分 64 | */ 65 | private final long dataCenterId; 66 | 67 | public IdWorker(){ 68 | 69 | this.dataCenterId = getDataCenterId(maxDataCenterId); 70 | this.workerId = getMaxWorkerId(dataCenterId, maxWorkerId); 71 | } 72 | /** 73 | * @param workerId 74 | * 工作机器ID 75 | * @param dataCenterId 76 | * 序列号 77 | */ 78 | public IdWorker(long workerId, long dataCenterId) { 79 | if (workerId > maxWorkerId || workerId < 0) { 80 | throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); 81 | } 82 | if (dataCenterId > maxDataCenterId || dataCenterId < 0) { 83 | throw new IllegalArgumentException(String.format("dataCenter Id can't be greater than %d or less than 0", maxDataCenterId)); 84 | } 85 | this.workerId = workerId; 86 | this.dataCenterId = dataCenterId; 87 | } 88 | /** 89 | * 获取下一个ID 90 | * 91 | * @return 92 | */ 93 | public synchronized long nextId() { 94 | long timestamp = timeGen(); 95 | if (timestamp < lastTimestamp) { 96 | throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 97 | } 98 | if (lastTimestamp == timestamp) { 99 | // 当前毫秒内,则+1 100 | sequence = (sequence + 1) & sequenceMask; 101 | if (sequence == 0) { 102 | // 当前毫秒内计数满了,则等待下一秒 103 | timestamp = tilNextMillis(lastTimestamp); 104 | } 105 | } else { 106 | sequence = 0L; 107 | } 108 | lastTimestamp = timestamp; 109 | // ID偏移组合生成最终的ID,并返回ID 110 | long nextId = ((timestamp - twEpoch) << timestampLeftShift) 111 | | (dataCenterId << dataCenterIdShift) 112 | | (workerId << workerIdShift) | sequence; 113 | return nextId; 114 | } 115 | private long tilNextMillis(final long lastTimestamp) { 116 | long timestamp = this.timeGen(); 117 | while (timestamp <= lastTimestamp) { 118 | timestamp = this.timeGen(); 119 | } 120 | return timestamp; 121 | } 122 | private long timeGen() { 123 | return System.currentTimeMillis(); 124 | } 125 | /** 126 | *

127 | * 获取 maxWorkerId 128 | *

129 | */ 130 | protected static long getMaxWorkerId(long dataCenterId, long maxWorkerId) { 131 | StringBuffer mpid = new StringBuffer(); 132 | mpid.append(dataCenterId); 133 | String name = ManagementFactory.getRuntimeMXBean().getName(); 134 | if (!name.isEmpty()) { 135 | /* 136 | * GET jvmPid 137 | */ 138 | mpid.append(name.split("@")[0]); 139 | } 140 | /* 141 | * MAC + PID 的 hashcode 获取16个低位 142 | */ 143 | return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); 144 | } 145 | /** 146 | *

147 | * 数据标识id部分 148 | *

149 | */ 150 | protected static long getDataCenterId(long maxDataCenterId) { 151 | long id = 0L; 152 | try { 153 | InetAddress ip = InetAddress.getLocalHost(); 154 | NetworkInterface network = NetworkInterface.getByInetAddress(ip); 155 | if (network == null) { 156 | id = 1L; 157 | } else { 158 | byte[] mac = network.getHardwareAddress(); 159 | id = ((0x000000FF & (long) mac[mac.length - 1]) 160 | | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; 161 | id = id % (maxDataCenterId + 1); 162 | } 163 | } catch (Exception e) { 164 | System.out.println(" getDataCenterId: " + e.getMessage()); 165 | } 166 | return id; 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/collection/StringUtil.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.collection; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.UUID; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | 14 | public class StringUtil { 15 | 16 | /** 17 | * 判断一个字符串是否为null或空值. 18 | * 19 | * @param str 指定的字符串 20 | * @return 21 | */ 22 | public static boolean isEmpty(String str) { 23 | return str == null || str.trim().length() == 0; 24 | } 25 | 26 | /** 27 | * 判断是否不是空 28 | * 29 | * @param str 指定的字符串 30 | * @return 31 | */ 32 | public static boolean isNotEmpty(String str){ 33 | return str!=null && str.trim().length()!=0; 34 | } 35 | 36 | /** 37 | * 是否是纯数字. 38 | * 39 | * @param str 指定的字符串 40 | * @return 41 | */ 42 | public static boolean isNumber(String str) { 43 | boolean isNumber = false; 44 | String expr = "^[0-9]+$"; 45 | if (str.matches(expr)) { 46 | isNumber = true; 47 | } 48 | return isNumber; 49 | } 50 | 51 | /** 52 | * 是否只是字母和数字. 53 | * 54 | * @param str 指定的字符串 55 | * @return 56 | */ 57 | public static boolean isNumberLetter(String str) { 58 | boolean isNoLetter = false; 59 | String expr = "^[A-Za-z0-9]+$"; 60 | if (str.matches(expr)) { 61 | isNoLetter = true; 62 | } 63 | return isNoLetter; 64 | } 65 | 66 | /** 67 | * 描述:手机号格式验证. 68 | * 69 | * @param str 指定的手机号码字符串 70 | * @return 是否为手机号码格式:是为true,否则false 71 | */ 72 | public static boolean isMobileNo(String str) { 73 | boolean isMobileNo = false; 74 | try { 75 | //13开头 76 | //Pattern p = Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$"); 77 | Pattern p = Pattern.compile("^(13|14|15|17|18)\\d{9}$"); 78 | Matcher m = p.matcher(str); 79 | isMobileNo = m.matches(); 80 | } catch (Exception e) { 81 | 82 | } 83 | return isMobileNo; 84 | } 85 | 86 | /** 87 | * 从输入流中获得String. 88 | * 89 | * @param is 输入流 90 | * @return 获得的String 91 | */ 92 | public static String convertStreamToString(InputStream is) { 93 | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 94 | StringBuilder sb = new StringBuilder(); 95 | String line = null; 96 | try { 97 | while ((line = reader.readLine()) != null) { 98 | sb.append(line + "\n"); 99 | } 100 | 101 | //最后一个\n删除 102 | if(sb.indexOf("\n")!=-1 && sb.lastIndexOf("\n") == sb.length()-1){ 103 | sb.delete(sb.lastIndexOf("\n"), sb.lastIndexOf("\n")+1); 104 | } 105 | 106 | } catch (IOException e) { 107 | e.printStackTrace(); 108 | } finally { 109 | try { 110 | is.close(); 111 | } catch (IOException e) { 112 | e.printStackTrace(); 113 | } 114 | } 115 | return sb.toString(); 116 | } 117 | 118 | /** 119 | * 根据指定的字符把源字符串分割成一个数组 120 | * 121 | * @param src 122 | * @return 123 | */ 124 | public static List parseString2ListByCustomerPattern(String pattern, String src) { 125 | if (src == null) { 126 | return null; 127 | } 128 | List list = new ArrayList(); 129 | String[] result = src.split(pattern); 130 | for (int i = 0; i < result.length; i++) { 131 | list.add(result[i]); 132 | } 133 | return list; 134 | } 135 | 136 | /** 137 | * 将字符串转化为int 138 | * 139 | * @param s 140 | * @return 141 | */ 142 | public static int toInt(String s) { 143 | int result = 0; 144 | if (!isEmpty(s)) { 145 | try { 146 | result = Integer.parseInt(s); 147 | } catch (Exception e) { 148 | // 非数值字符串默认返回0 149 | } 150 | } 151 | return result; 152 | } 153 | 154 | 155 | 156 | /** 157 | * 将字符串转化为long 158 | * 159 | * @param s 160 | * @return 161 | */ 162 | public static long toLong(String s) { 163 | long result = 0; 164 | if (!isEmpty(s)) { 165 | try { 166 | result = Long.parseLong(s); 167 | } catch (Exception e) { 168 | 169 | } 170 | } 171 | return result; 172 | } 173 | 174 | /** 175 | * 将字符串转化为float 176 | * 177 | * @param s 178 | * @return 179 | */ 180 | public static Float toFloat(String s) { 181 | //0.00 182 | float result = new Float(0); 183 | if (!isEmpty(s)) { 184 | try { 185 | result = Float.parseFloat(s); 186 | } catch (Exception e) { 187 | 188 | } 189 | } 190 | return result; 191 | } 192 | 193 | /** 194 | * 将字符串转化为double 195 | * 196 | * @param s 197 | * @return 198 | */ 199 | public static double toDouble(String s) { 200 | double result = 0; 201 | if (!isEmpty(s)) { 202 | try { 203 | result = Double.parseDouble(s); 204 | } catch (Exception e) { 205 | 206 | } 207 | } 208 | return result; 209 | } 210 | 211 | public static String getUUID() { 212 | return UUID.randomUUID().toString(); 213 | } 214 | 215 | public static String getSimpleUUID() { 216 | return UUID.randomUUID().toString().replace("-", ""); 217 | } 218 | 219 | 220 | 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/collection/ConvertUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.collection; 2 | 3 | import java.sql.Timestamp; 4 | import java.text.SimpleDateFormat; 5 | 6 | /** 7 | * 转换工具类
8 | * 若待转换值为null或者出现异常,则使用默认值 9 | * 10 | * @Author:chenssy 11 | * @date:2016年5月21日 上午10:18:12 12 | */ 13 | public class ConvertUtils { 14 | 15 | /** 16 | * 字符串转换为int 17 | * 18 | * @author:chenssy 19 | * @date : 2016年5月21日 上午10:16:27 20 | * 21 | * @param str 22 | * 待转换的字符串 23 | * @param defaultValue 24 | * 默认值 25 | * @return 26 | */ 27 | public static int strToInt(String str, int defaultValue) { 28 | try { 29 | defaultValue = Integer.parseInt(str); 30 | } catch (Exception localException) { 31 | } 32 | return defaultValue; 33 | } 34 | 35 | /** 36 | * String转换为long 37 | * 38 | * @author:chenssy 39 | * @date : 2016年5月21日 上午10:18:44 40 | * 41 | * @param str 42 | * 待转换字符串 43 | * @param defaultValue 44 | * 默认值 45 | * @return 46 | */ 47 | public static long strToLong(String str, long defaultValue) { 48 | try { 49 | defaultValue = Long.parseLong(str); 50 | } catch (Exception localException) { 51 | } 52 | return defaultValue; 53 | } 54 | 55 | /** 56 | * 字符串转换为float 57 | * 58 | * @author:chenssy 59 | * @date : 2016年5月21日 上午10:19:12 60 | * 61 | * @param str 62 | * 63 | * @param defaultValue 64 | * @return 65 | */ 66 | public static float strToFloat(String str, float defaultValue) { 67 | try { 68 | defaultValue = Float.parseFloat(str); 69 | } catch (Exception localException) { 70 | } 71 | return defaultValue; 72 | } 73 | 74 | /** 75 | * String转换为Double 76 | * 77 | * @author:chenssy 78 | * @date : 2016年5月21日 上午10:21:59 79 | * 80 | * @param str 81 | * 待转换字符串 82 | * @param defaultValue 83 | * 默认值 84 | * @return 85 | */ 86 | public static double strToDouble(String str, double defaultValue) { 87 | try { 88 | defaultValue = Double.parseDouble(str); 89 | } catch (Exception localException) { 90 | } 91 | return defaultValue; 92 | } 93 | 94 | /** 95 | * 字符串转换日期 96 | * 97 | * @author:chenssy 98 | * @date : 2016年5月21日 上午10:27:01 99 | * 100 | * @param str 101 | * 待转换的字符串 102 | * @param defaultValue 103 | * 默认日期 104 | * @return 105 | */ 106 | public static java.util.Date strToDate(String str,java.util.Date defaultValue) { 107 | return strToDate(str, "yyyy-MM-dd HH:mm:ss", defaultValue); 108 | } 109 | 110 | /** 111 | * 字符串转换为指定格式的日期 112 | * 113 | * @author:chenssy 114 | * @date : 2016年5月21日 上午10:27:24 115 | * 116 | * @param str 117 | * 待转换的字符串 118 | * @param format 119 | * 日期格式 120 | * @param defaultValue 121 | * 默认日期 122 | * @return 123 | */ 124 | public static java.util.Date strToDate(String str, String format,java.util.Date defaultValue) { 125 | SimpleDateFormat fmt = new SimpleDateFormat(format); 126 | try { 127 | defaultValue = fmt.parse(str); 128 | } catch (Exception localException) { 129 | } 130 | return defaultValue; 131 | } 132 | 133 | /** 134 | * 日期转换为字符串 135 | * 136 | * @author:chenssy 137 | * @date : 2016年5月21日 上午10:28:05 138 | * 139 | * @param date 140 | * 待转换的日期 141 | * @param defaultValue 142 | * 默认字符串 143 | * @return 144 | */ 145 | public static String dateToStr(java.util.Date date, String defaultValue) { 146 | return dateToStr(date, "yyyy-MM-dd HH:mm:ss", defaultValue); 147 | } 148 | 149 | /** 150 | * 日期转换为指定格式的字符串 151 | * 152 | * @author:chenssy 153 | * @date : 2016年5月21日 上午10:28:51 154 | * 155 | * @param date 156 | * 待转换的日期 157 | * @param format 158 | * 指定格式 159 | * @param defaultValue 160 | * 默认值 161 | * @return 162 | */ 163 | public static String dateToStr(java.util.Date date, String format, String defaultValue) { 164 | SimpleDateFormat sdf = new SimpleDateFormat(format); 165 | try { 166 | defaultValue = sdf.format(date); 167 | } catch (Exception localException) { 168 | } 169 | return defaultValue; 170 | } 171 | 172 | /** 173 | * 如果字符串为空则使用默认字符串 174 | * 175 | * @author:chenssy 176 | * @date : 2016年5月21日 上午10:29:35 177 | * 178 | * @param str 179 | * 字符串 180 | * @param defaultValue 181 | * 默认值 182 | * @return 183 | */ 184 | public static String strToStr(String str, String defaultValue) { 185 | if ((str != null) && (!(str.isEmpty()))) 186 | defaultValue = str; 187 | return defaultValue; 188 | } 189 | 190 | /** 191 | * util date 转换为 sqldate 192 | * 193 | * @author:chenssy 194 | * @date : 2016年5月21日 上午10:30:09 195 | * 196 | * @param date 197 | * @return 198 | */ 199 | public static java.sql.Date dateToSqlDate(java.util.Date date) { 200 | return new java.sql.Date(date.getTime()); 201 | } 202 | 203 | /** 204 | * sql date 转换为 util date 205 | * 206 | * @author:chenssy 207 | * @date : 2016年5月21日 上午10:30:26 208 | * 209 | * @param date 210 | * @return 211 | */ 212 | public static java.util.Date sqlDateToDate(java.sql.Date date) { 213 | return new java.util.Date(date.getTime()); 214 | } 215 | 216 | /** 217 | * date 转换为 timestamp 218 | * 219 | * @author:chenssy 220 | * @date : 2016年5月21日 上午10:30:51 221 | * 222 | * @param date 223 | * @return 224 | */ 225 | public static Timestamp dateToSqlTimestamp(java.util.Date date) { 226 | return new Timestamp(date.getTime()); 227 | } 228 | 229 | /** 230 | * timestamp 转换为date 231 | * 232 | * @author:chenssy 233 | * @date : 2016年5月21日 上午10:31:13 234 | * 235 | * @param date 236 | * @return 237 | */ 238 | public static java.util.Date qlTimestampToDate(Timestamp date) { 239 | return new java.util.Date(date.getTime()); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/tabooed/TabooedTools.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.tabooed; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.HashMap; 6 | import java.util.LinkedHashSet; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Set; 10 | 11 | /** 12 | * 敏感词汇过滤工具类,使用了 DFA 算法来实现词汇过滤。 13 | * 14 | * @author xuan 15 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午10:23:37 $ 16 | */ 17 | public class TabooedTools { 18 | 19 | private static final TabooedTools instance = new TabooedTools(); 20 | 21 | private Collection tabooedWords; 22 | private final Node rootNode = new Node('R'); 23 | 24 | private TabooedTools() { 25 | initialize(); 26 | } 27 | 28 | public static TabooedTools getInstance() { 29 | return instance; 30 | } 31 | 32 | public synchronized void setTabooedWords(Collection tabooedWords) { 33 | this.tabooedWords = tabooedWords; 34 | claerNode(); 35 | // 创建字符节点树 36 | createNodeTree(); 37 | 38 | } 39 | 40 | public synchronized void initialize() { 41 | TabooedWords tws = new TabooedWords(); 42 | tws.initialize(); 43 | tabooedWords = tws.getTabooedWords(); 44 | 45 | // 创建字符节点树 46 | createNodeTree(); 47 | } 48 | 49 | public List getTabooedWords(String content) { 50 | Set tabooedWords4Content = new LinkedHashSet(); 51 | searchWord(content, tabooedWords4Content); 52 | return new ArrayList(tabooedWords4Content); 53 | } 54 | 55 | private void searchWord(String content, Set tabooedWords4Content) { 56 | // 这个是检测的中间状态列表 57 | List tempWords = new ArrayList(); 58 | 59 | int index = 0; 60 | Node node = rootNode; 61 | char[] chars = content.toCharArray(); 62 | while (index < chars.length) { 63 | char currentChar = chars[index]; 64 | 65 | node = findNode(node, currentChar); 66 | if (node == null) { // 如果找不到后续节点则进行回溯 67 | node = rootNode; 68 | index = index - tempWords.size(); 69 | tempWords.clear(); 70 | } 71 | else if (isNodeFinish(node, index, chars, tempWords.size())) { 72 | // 如果节点终结的话, 就将敏感词汇保存到结果集中 73 | StringBuilder sb = new StringBuilder(); 74 | tempWords.add(currentChar); 75 | for (char c : tempWords) { 76 | sb.append(c); 77 | } 78 | tabooedWords4Content.add(sb.toString()); 79 | 80 | index = index - tempWords.size() + 1; 81 | tempWords.clear(); 82 | node = rootNode; 83 | } 84 | else { // 找到匹配的敏感字符后将当前字符保存到临时列表中 85 | tempWords.add(currentChar); 86 | } 87 | 88 | index++; 89 | } 90 | } 91 | 92 | /** 93 | * 判断字符节点是否终结. 94 | * 95 | * @param node 96 | * @param index 97 | * @param chars 98 | * @param matchCount 99 | * @return true/false 100 | */ 101 | private boolean isNodeFinish(Node node, int index, char[] chars, int matchCount) { 102 | boolean isFinish = (node.flag == Node.FLAG_FINISH); 103 | if (!isFinish) { 104 | return false; 105 | } 106 | 107 | isFinish = (index == chars.length - 1 || !isAlpha(chars[index + 1])); 108 | if (!isFinish) { 109 | return false; 110 | } 111 | 112 | if (index - 1 - matchCount < 0) { 113 | return true; 114 | } 115 | 116 | return (index == 1 || !isAlpha(chars[index - 1 - matchCount])); 117 | } 118 | 119 | /** 120 | * 创建敏感字符的节点树 121 | */ 122 | private void createNodeTree() { 123 | for (String str : tabooedWords) { 124 | char[] chars = str.toCharArray(); 125 | if (chars.length > 0) { 126 | insertNode(rootNode, chars, 0); 127 | } 128 | } 129 | } 130 | 131 | /** 132 | * 插入字符节点. 133 | * 134 | * @param parent 135 | * 父节点 136 | * @param chars 137 | * 过滤字符串的char数组 138 | * @param index 139 | * 待插入字符的索引 140 | */ 141 | private void insertNode(Node parent, char[] chars, int index) { 142 | Node node = findNode(parent, chars[index]); 143 | 144 | // 如果找不到已经存在的节点, 则创建一个节点 145 | if (node == null) { 146 | node = new Node(chars[index]); 147 | parent.addChild(node); 148 | } 149 | 150 | // 如果是最后一个字符, 则将节点标记为结束 151 | if (index == (chars.length - 1)) { 152 | node.flag = Node.FLAG_FINISH; 153 | } 154 | else { 155 | insertNode(node, chars, ++index); 156 | } 157 | } 158 | 159 | /** 160 | * 清除根节点的子节点 161 | */ 162 | private void claerNode() { 163 | rootNode.getChilds().clear(); 164 | } 165 | 166 | /** 167 | * 查找字符节点. 168 | * 169 | * @param parent 170 | * 父节点 171 | * @param c 172 | * 字符 173 | * @return 返回和字符所匹配的节点 174 | */ 175 | private Node findNode(Node parent, char c) { 176 | if (c >= 'A' && c <= 'Z') { 177 | c = (char) (c + 32); 178 | } 179 | 180 | Node node = parent.getChild(c); 181 | return node; 182 | } 183 | 184 | /** 185 | * 判断字符是否是英文字母. 186 | * 187 | * @param c 188 | * 字符 189 | * @return true/false 190 | */ 191 | private static boolean isAlpha(char c) { 192 | return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'; 193 | } 194 | 195 | /** 196 | * 表示一个字符节点的类. 197 | */ 198 | private static class Node { 199 | 200 | static final int FLAG_CONTINUE = 0; // 标记敏感字符延续 201 | static final int FLAG_FINISH = 1; // 标记敏感字符终结 202 | 203 | char c; 204 | int flag; 205 | 206 | Map nodeMap = new HashMap(); 207 | 208 | Node(char c) { 209 | this(c, FLAG_CONTINUE); 210 | } 211 | 212 | Node(char c, int flag) { 213 | this.c = c; 214 | this.flag = flag; 215 | } 216 | 217 | void addChild(Node node) { 218 | nodeMap.put(node.c, node); 219 | } 220 | 221 | Node getChild(char c) { 222 | return nodeMap.get(c); 223 | } 224 | 225 | Map getChilds() { 226 | return nodeMap; 227 | } 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/validate/IdcardValidator.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.validate; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | /** 6 | * --15位身份证号码:第7、8位为出生年份(两位数),第9、10位为出生月份,第11、12位代表出生日期,第15位代表性别,奇数为男,偶数为女。 7 | * --18位身份证号码 8 | * :第7、8、9、10位为出生年份(四位数),第11、第12位为出生月份,第13、14位代表出生日期,第17位代表性别,奇数为男,偶数为女。 9 | * 10 | * @Author:chenssy 11 | * @date:2016年6月1日 下午12:29:41 12 | */ 13 | public class IdcardValidator { 14 | 15 | /** 16 | * 省,直辖市代码表: { 11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古", 17 | * 21:"辽宁",22:"吉林",23:"黑龙江",31:"上海",32:"江苏", 18 | * 33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东",41:"河南", 19 | * 42:"湖北",43:"湖南",44:"广东",45:"广西",46:"海南",50:"重庆", 20 | * 51:"四川",52:"贵州",53:"云南",54:"西藏",61:"陕西",62:"甘肃", 21 | * 63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"} 22 | */ 23 | 24 | // 每位加权因子 25 | private static int power[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; 26 | 27 | /** 28 | * 验证身份证是否合法 29 | * 30 | * @param idcard 31 | * @return 32 | * @author : chenssy 33 | * @date : 2016年6月1日 下午12:30:03 34 | */ 35 | @SuppressWarnings("static-access") 36 | public boolean isValidatedAllIdcard(String idcard) { 37 | return isValidate18Idcard(idcard); 38 | } 39 | 40 | /** 41 | *

42 | * 判断18位身份证的合法性 43 | *

44 | * 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。 45 | * 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。 46 | *

47 | * 顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同 日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配 给女性。 48 | *

49 | *

50 | * 1.前1、2位数字表示:所在省份的代码; 2.第3、4位数字表示:所在城市的代码; 3.第5、6位数字表示:所在区县的代码; 51 | * 4.第7~14位数字表示:出生年、月、日; 5.第15、16位数字表示:所在地的派出所的代码; 52 | * 6.第17位数字表示性别:奇数表示男性,偶数表示女性; 53 | * 7.第18位数字是校检码:也有的说是个人信息码,一般是随计算机的随机产生,用来检验身份证的正确性。校检码可以是0~9的数字,有时也用x表示。 54 | *

55 | *

56 | * 第十八位数字(校验码)的计算方法为: 1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 57 | * 2 1 6 3 7 9 10 5 8 4 2 58 | *

59 | *

60 | * 2.将这17位数字和系数相乘的结果相加。 61 | *

62 | *

63 | * 3.用加出来和除以11,看余数是多少? 64 | *

65 | * 4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 66 | * 2。 67 | *

68 | * 5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。 69 | *

70 | * 71 | * @param idcard 待验证的身份证 72 | * @return 73 | * @author : chenssy 74 | * @date : 2016年6月1日 下午12:31:10 75 | */ 76 | public static boolean isValidate18Idcard(String idcard) { 77 | // 非18位为假 78 | if (idcard.length() != 18) { 79 | return false; 80 | } 81 | // 获取前17位 82 | String idcard17 = idcard.substring(0, 17); 83 | // 获取第18位 84 | String idcard18Code = idcard.substring(17, 18); 85 | char c[] = null; 86 | String checkCode = ""; 87 | // 是否都为数字 88 | if (isDigital(idcard17)) { 89 | c = idcard17.toCharArray(); 90 | } else { 91 | return false; 92 | } 93 | 94 | if (null != c) { 95 | int bit[] = new int[idcard17.length()]; 96 | bit = converCharToInt(c); 97 | int sum17 = 0; 98 | sum17 = getPowerSum(bit); 99 | // 将和值与11取模得到余数进行校验码判断 100 | checkCode = getCheckCodeBySum(sum17); 101 | if (null == checkCode) { 102 | return false; 103 | } 104 | // 将身份证的第18位与算出来的校码进行匹配,不相等就为假 105 | if (!idcard18Code.equalsIgnoreCase(checkCode)) { 106 | return false; 107 | } 108 | } 109 | 110 | return true; 111 | } 112 | 113 | /** 114 | * 18位身份证号码的基本数字和位数验校 115 | * 116 | * @param idcard 待验证的身份证 117 | * @return 118 | * @author : chenssy 119 | * @date : 2016年6月1日 下午12:31:49 120 | */ 121 | public static boolean is18Idcard(String idcard) { 122 | return Pattern.matches("^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([\\d|x|X]{1})$", idcard); 123 | } 124 | 125 | /** 126 | * 数字验证 127 | * 128 | * @param str 129 | * @return 130 | * @author : chenssy 131 | * @date : 2016年6月1日 下午12:32:18 132 | */ 133 | private static boolean isDigital(String str) { 134 | return str == null || "".equals(str) ? false : str.matches("^[0-9]*$"); 135 | } 136 | 137 | /** 138 | * 将身份证的每位和对应位的加权因子相乘之后,再得到和值 139 | * 140 | * @param bit 141 | * @return 142 | * @author : chenssy 143 | * @date : 2016年6月1日 下午12:32:34 144 | */ 145 | private static int getPowerSum(int[] bit) { 146 | int sum = 0; 147 | if (power.length != bit.length) { 148 | return sum; 149 | } 150 | 151 | for (int i = 0; i < bit.length; i++) { 152 | for (int j = 0; j < power.length; j++) { 153 | if (i == j) { 154 | sum = sum + bit[i] * power[j]; 155 | } 156 | } 157 | } 158 | 159 | return sum; 160 | } 161 | 162 | /** 163 | * 将和值与11取模得到余数进行校验码判断 164 | * 165 | * @param sum17 166 | * @return 167 | * @author : chenssy 168 | * @date : 2016年6月1日 下午12:32:51 169 | */ 170 | private static String getCheckCodeBySum(int sum17) { 171 | String checkCode = null; 172 | switch (sum17 % 11) { 173 | case 10: 174 | checkCode = "2"; 175 | break; 176 | case 9: 177 | checkCode = "3"; 178 | break; 179 | case 8: 180 | checkCode = "4"; 181 | break; 182 | case 7: 183 | checkCode = "5"; 184 | break; 185 | case 6: 186 | checkCode = "6"; 187 | break; 188 | case 5: 189 | checkCode = "7"; 190 | break; 191 | case 4: 192 | checkCode = "8"; 193 | break; 194 | case 3: 195 | checkCode = "9"; 196 | break; 197 | case 2: 198 | checkCode = "x"; 199 | break; 200 | case 1: 201 | checkCode = "0"; 202 | break; 203 | case 0: 204 | checkCode = "1"; 205 | break; 206 | } 207 | return checkCode; 208 | } 209 | 210 | /** 211 | * 将字符数组转为整型数组 212 | * 213 | * @param c 214 | * @return 215 | * @throws NumberFormatException 216 | * @author : chenssy 217 | * @date : 2016年6月1日 下午12:33:22 218 | */ 219 | private static int[] converCharToInt(char[] c) throws NumberFormatException { 220 | int[] a = new int[c.length]; 221 | int k = 0; 222 | for (char temp : c) { 223 | a[k++] = Integer.parseInt(String.valueOf(temp)); 224 | } 225 | return a; 226 | } 227 | 228 | /** 229 | * @param idno 230 | * @return 身份证信息中代表性别的数值 231 | */ 232 | public static int getUserSex(String idno) { 233 | String sex = "1"; 234 | if (idno != null) { 235 | if (idno.length() > 15) { 236 | sex = idno.substring(16, 17); 237 | } 238 | } 239 | 240 | return Integer.parseInt(sex) % 2 == 0 ? 0 : 1; 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/date/TimeUtil.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.date; 2 | 3 | import me.xueyao.collection.StringUtil; 4 | 5 | import java.text.ParseException; 6 | import java.text.SimpleDateFormat; 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | 10 | public class TimeUtil { 11 | 12 | private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(); 13 | public final static String FORMAT_YEAR = "yyyy年"; 14 | public final static String FORMAT_DATE = "yyyy-MM-dd"; 15 | public final static String FORMAT_DATE_YYMMDD = "yyyyMMdd"; 16 | public final static String FORMAT_TIME = "hh:mm"; 17 | public final static String FORMAT_DATE_TIME = "yyyy-MM-dd hh:mm"; 18 | public final static String FORMAT_DATE_TIME_2 = "yyyy-MM-dd HH:mm"; 19 | public final static String TIME_FORMAT_SHOW_MILLISECOND_WITH_COLON = "yyyy-MM-dd HH:mm:ss"; 20 | public final static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; 21 | public final static String FORMAT_MONTH_DAY_TIME = "MM月dd日 hh:mm"; 22 | public final static String MONTH_DAY = "MMdd"; 23 | private static final int YEAR = 365 * 24 * 60 * 60;// 年 24 | private static final int MONTH = 30 * 24 * 60 * 60;// 月 25 | private static final int DAY = 24 * 60 * 60;// 天 26 | private static final int HOUR = 60 * 60;// 小时 27 | private static final int MINUTE = 60;// 分钟 28 | 29 | /** 30 | * 获取当前日期的指定格式的字符串:new Date()--->String 31 | * 32 | * @param format 33 | */ 34 | public static String getCurrentTime(String format) { 35 | if (StringUtil.isEmpty(format)) { 36 | simpleDateFormat.applyPattern(FORMAT_DATE_TIME); 37 | } else { 38 | simpleDateFormat.applyPattern(format); 39 | } 40 | return simpleDateFormat.format(new Date()); 41 | } 42 | 43 | /** 44 | * 将long类型的时间格式化:long--->String 45 | * 46 | * @param format 47 | * @param date 48 | */ 49 | public static String formatTimes(String format, long date) { 50 | if (StringUtil.isEmpty(format)) { 51 | simpleDateFormat.applyPattern(FORMAT_DATE_TIME); 52 | } else { 53 | simpleDateFormat.applyPattern(format); 54 | } 55 | return simpleDateFormat.format(new Date(date)); 56 | } 57 | 58 | /** 59 | * 将日期字符串以指定格式转换为Date:String--->Date 60 | * 61 | * @param time 62 | * 日期字符串 63 | * @param format 64 | * 指定的日期格式,若为null或""则使用指定的格式"yyyy-MM-dd HH:MM" 65 | * @return 66 | */ 67 | public static Date getTimeFromString(String timeStr, String format) { 68 | try { 69 | return StringUtil.isEmpty(timeStr) ? null : new SimpleDateFormat(format).parse(timeStr); 70 | } catch (ParseException e) { 71 | e.printStackTrace(); 72 | } 73 | return null; 74 | } 75 | 76 | /** 77 | * 将Date以指定格式转换为日期时间字符串:Date---->String 78 | * 79 | * @param date 80 | * 日期 81 | * @param format 82 | * 指定的日期时间格式,若为null或""则使用指定的格式"yyyy-MM-dd HH:MM" 83 | * @return 84 | */ 85 | public static String getStringFromTime(Date time, String format) { 86 | if (format == null || format.trim().equals("")) { 87 | simpleDateFormat.applyPattern(FORMAT_DATE_TIME); 88 | } else { 89 | simpleDateFormat.applyPattern(format); 90 | } 91 | return simpleDateFormat.format(time); 92 | } 93 | 94 | // -----------------使用频率较少----------------------------------// 95 | 96 | /** 97 | * 根据时间戳获取描述性时间,如3分钟前,1天前 98 | * 99 | * @param timestamp 100 | * 时间戳 单位为毫秒 101 | * @return 时间字符串 102 | */ 103 | public static String getDescriptionTimeFromTimestamp(long timestamp) { 104 | long currentTime = System.currentTimeMillis(); 105 | long timeGap = (currentTime - timestamp) / 1000;// 与现在时间相差秒数 106 | //GlobalLog.MY_LOGGER.debug("timeGap: " + timeGap); 107 | String timeStr = null; 108 | if (timeGap > YEAR) { 109 | timeStr = timeGap / YEAR + "年前"; 110 | } else if (timeGap > MONTH) { 111 | timeStr = timeGap / MONTH + "个月前"; 112 | } else if (timeGap > DAY) {// 1天以上 113 | timeStr = timeGap / DAY + "天前"; 114 | } else if (timeGap > HOUR) {// 1小时-24小时 115 | timeStr = timeGap / HOUR + "小时前"; 116 | } else if (timeGap > MINUTE) {// 1分钟-59分钟 117 | timeStr = timeGap / MINUTE + "分钟前"; 118 | } else {// 1秒钟-59秒钟 119 | timeStr = "刚刚"; 120 | } 121 | return timeStr; 122 | } 123 | 124 | /** 125 | * 根据时间戳获取时间字符串,并根据指定的时间分割数partionSeconds来自动判断返回描述性时间还是指定格式的时间 126 | * 127 | * @param timestamp 128 | * 时间戳 单位是毫秒 129 | * @param partionSeconds 130 | * 时间分割线,当现在时间与指定的时间戳的秒数差大于这个分割线时则返回指定格式时间,否则返回描述性时间 131 | * @param format 132 | * @return 133 | */ 134 | public static String getMixTimeFromTimestamp(long timestamp, 135 | long partionSeconds, String format) { 136 | long currentTime = System.currentTimeMillis(); 137 | long timeGap = (currentTime - timestamp) / 1000;// 与现在时间相差秒数 138 | if (timeGap <= partionSeconds) { 139 | return getDescriptionTimeFromTimestamp(timestamp); 140 | } else { 141 | return getFormatTimeFromTimestamp(timestamp, format); 142 | } 143 | } 144 | 145 | /** 146 | * 根据时间戳获取指定格式的时间,如2011-11-30 08:40 147 | * 148 | * @param timestamp 时间戳 单位为毫秒 149 | * @param format 指定格式 如果为null或空串则使用默认格式"yyyy-MM-dd HH:MM" 150 | * @return 151 | */ 152 | public static String getFormatTimeFromTimestamp(long timestamp, 153 | String format) { 154 | if (StringUtil.isEmpty(format)) { 155 | simpleDateFormat.applyPattern(FORMAT_DATE); 156 | int currentYear = Calendar.getInstance().get(Calendar.YEAR); 157 | int year = Integer.valueOf(simpleDateFormat.format( 158 | new Date(timestamp)).substring(0, 4)); 159 | if (currentYear == year) {// 如果为今年则不显示年份 160 | simpleDateFormat.applyPattern(FORMAT_MONTH_DAY_TIME); 161 | } else { 162 | simpleDateFormat.applyPattern(FORMAT_DATE_TIME); 163 | } 164 | } else { 165 | simpleDateFormat.applyPattern(format); 166 | } 167 | return simpleDateFormat.format(new Date(timestamp)); 168 | } 169 | 170 | 171 | /** 172 | * 计算剩余时长 173 | * 174 | * 超过1小时,分钟不参入计算 175 | * 不足小时,按分钟计算 176 | * 不足1分钟,按1分钟算 177 | * 178 | */ 179 | public static String timeLeft(Date startDate,long validTime) { 180 | String timeLeftStr = null; 181 | 182 | long currentTime = System.currentTimeMillis(); 183 | long startTime = startDate.getTime(); 184 | 185 | if(startTime + validTime > currentTime) { //未超时 186 | 187 | long timeLeftValue = validTime + startDate.getTime() - currentTime; 188 | long second = timeLeftValue / 1000; 189 | if(second < 60) { //1分钟内,显示1分钟 190 | timeLeftStr = "1分钟"; 191 | } else { 192 | long minutes = second / 60; 193 | if(minutes > 60) { //超过1个小时 194 | long hour = minutes / 60; 195 | timeLeftStr = hour + "小时"; 196 | } else { 197 | timeLeftStr = minutes + "分钟"; 198 | } 199 | } 200 | 201 | } else { 202 | //订单超时了 203 | timeLeftStr = "小于1分钟"; 204 | } 205 | return timeLeftStr; 206 | } 207 | 208 | //由出生日期获得年龄 209 | public static int getAge(Date birthDay) { 210 | Calendar cal = Calendar.getInstance(); 211 | 212 | if (cal.before(birthDay)) { 213 | throw new IllegalArgumentException( 214 | "The birthDay is before Now.It's unbelievable!"); 215 | } 216 | int yearNow = cal.get(Calendar.YEAR); 217 | int monthNow = cal.get(Calendar.MONTH); 218 | int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH); 219 | cal.setTime(birthDay); 220 | 221 | int yearBirth = cal.get(Calendar.YEAR); 222 | int monthBirth = cal.get(Calendar.MONTH); 223 | int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH); 224 | 225 | int age = yearNow - yearBirth; 226 | 227 | if (monthNow <= monthBirth) { 228 | if (monthNow == monthBirth) { 229 | if (dayOfMonthNow < dayOfMonthBirth){ 230 | age--; 231 | } 232 | } else{ 233 | age--; 234 | } 235 | } 236 | return age; 237 | } 238 | 239 | } 240 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/validate/RegexUtils.java: -------------------------------------------------------------------------------- 1 | 2 | package me.xueyao.validate; 3 | 4 | 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | /** 9 | * 正则工具类 10 | * @author liushen 11 | * 12 | */ 13 | public class RegexUtils { 14 | 15 | /** 16 | * 验证Email 17 | * @param email email地址,格式:zhangsan@sina.com,zhangsan@xxx.com.cn,xxx代表邮件服务商 18 | * @return 验证成功返回true,验证失败返回false 19 | */ 20 | public static boolean checkEmail(String email) { 21 | String regex = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?"; 22 | return Pattern.matches(regex, email); 23 | } 24 | 25 | /** 26 | * 验证身份证号码 27 | * @param idCard 居民身份证号码18位,第一位不能为0,最后一位可能是数字或字母,中间16位为数字 \d同[0-9] 28 | * @return 验证成功返回true,验证失败返回false 29 | */ 30 | public static boolean checkIdCard(String idCard) { 31 | String regex = "[1-9]\\d{16}[a-zA-Z0-9]{1}"; 32 | return Pattern.matches(regex,idCard); 33 | } 34 | 35 | /** 36 | * 验证手机号码(支持国际格式,+86135xxxx...(中国内地),+00852137xxxx...(中国香港)) 37 | * @param mobile 移动、联通、电信运营商的号码段 38 | *

移动的号段:134(0-8)、135、136、137、138、139、147(预计用于TD上网卡) 39 | *、150、151、152、157(TD专用)、158、159、187(未启用)、188(TD专用)

40 | *

联通的号段:130、131、132、155、156(世界风专用)、185(未启用)、186(3g)

41 | *

电信的号段:133、153、180(未启用)、189

42 | * @return 验证成功返回true,验证失败返回false 43 | */ 44 | public static boolean checkMobile(String mobile) { 45 | String regex = "(\\+\\d+)?1[3456789]\\d{9}$"; 46 | return Pattern.matches(regex,mobile); 47 | } 48 | 49 | /** 50 | * 验证固定电话号码 51 | * @param phone 电话号码,格式:国家(地区)电话代码 + 区号(城市代码) + 电话号码,如:+8602085588447 52 | *

国家(地区) 代码 :标识电话号码的国家(地区)的标准国家(地区)代码。它包含从 0 到 9 的一位或多位数字, 53 | * 数字之后是空格分隔的国家(地区)代码。

54 | *

区号(城市代码):这可能包含一个或多个从 0 到 9 的数字,地区或城市代码放在圆括号—— 55 | * 对不使用地区或城市代码的国家(地区),则省略该组件。

56 | *

电话号码:这包含从 0 到 9 的一个或多个数字

57 | * @return 验证成功返回true,验证失败返回false 58 | */ 59 | public static boolean checkPhone(String phone) { 60 | String regex = "(\\+\\d+)?(\\d{3,4}\\-?)?\\d{7,8}$"; 61 | return Pattern.matches(regex, phone); 62 | } 63 | 64 | /** 65 | * 验证整数(正整数和负整数) 66 | * @param digit 一位或多位0-9之间的整数 67 | * @return 验证成功返回true,验证失败返回false 68 | */ 69 | public static boolean checkDigit(String digit) { 70 | String regex = "\\-?[1-9]\\d+"; 71 | return Pattern.matches(regex,digit); 72 | } 73 | 74 | /** 75 | * 验证整数和浮点数(正负整数和正负浮点数) 76 | * @param decimals 一位或多位0-9之间的浮点数,如:1.23,233.30 77 | * @return 验证成功返回true,验证失败返回false 78 | */ 79 | public static boolean checkDecimals(String decimals) { 80 | String regex = "\\-?[1-9]\\d+(\\.\\d+)?"; 81 | return Pattern.matches(regex,decimals); 82 | } 83 | 84 | /** 85 | * 验证空白字符 86 | * @param blankSpace 空白字符,包括:空格、\t、\n、\r、\f、\x0B 87 | * @return 验证成功返回true,验证失败返回false 88 | */ 89 | public static boolean checkBlankSpace(String blankSpace) { 90 | String regex = "\\s+"; 91 | return Pattern.matches(regex,blankSpace); 92 | } 93 | 94 | /** 95 | * 验证中文 96 | * @param chinese 中文字符 97 | * @return 验证成功返回true,验证失败返回false 98 | */ 99 | public static boolean checkChinese(String chinese) { 100 | String regex = "^[\u4E00-\u9FA5]+$"; 101 | return Pattern.matches(regex,chinese); 102 | } 103 | 104 | /** 105 | * 验证日期(年月日) 106 | * @param birthday 日期,格式:1992-09-03,或1992.09.03 107 | * @return 验证成功返回true,验证失败返回false 108 | */ 109 | public static boolean checkBirthday(String birthday) { 110 | String regex = "[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}"; 111 | return Pattern.matches(regex,birthday); 112 | } 113 | 114 | /** 115 | * 验证URL地址 116 | * @param url 格式:http://blog.csdn.net:80/xyang81/article/details/7705960? 或 http://www.csdn.net:80 117 | * @return 验证成功返回true,验证失败返回false 118 | */ 119 | public static boolean checkURL(String url) { 120 | String regex = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?"; 121 | return Pattern.matches(regex, url); 122 | } 123 | 124 | /** 125 | *
126 |      * 获取网址 URL 的一级域名
127 |      * http://detail.tmall.com/item.htm?spm=a230r.1.10.44.1xpDSH&id=15453106243&_u=f4ve1uq1092 ->> tmall.com
128 |      * 
129 | * 130 | * @param url 131 | * @return 132 | */ 133 | public static String getDomain(String url) { 134 | Pattern p = Pattern.compile("(?<=http://|\\.)[^.]*?\\.(com|cn|net|org|biz|info|cc|tv)", Pattern.CASE_INSENSITIVE); 135 | // 获取完整的域名 136 | // Pattern p=Pattern.compile("[^//]*?\\.(com|cn|net|org|biz|info|cc|tv)", Pattern.CASE_INSENSITIVE); 137 | Matcher matcher = p.matcher(url); 138 | matcher.find(); 139 | return matcher.group(); 140 | } 141 | /** 142 | * 匹配中国邮政编码 143 | * @param postcode 邮政编码 144 | * @return 验证成功返回true,验证失败返回false 145 | */ 146 | public static boolean checkPostcode(String postcode) { 147 | String regex = "[1-9]\\d{5}"; 148 | return Pattern.matches(regex, postcode); 149 | } 150 | 151 | /** 152 | * 匹配IP地址(简单匹配,格式,如:192.168.1.1,127.0.0.1,没有匹配IP段的大小) 153 | * @param ipAddress IPv4标准地址 154 | * @return 验证成功返回true,验证失败返回false 155 | */ 156 | public static boolean checkIpAddress(String ipAddress) { 157 | String regex = "[1-9](\\d{1,2})?\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))"; 158 | return Pattern.matches(regex, ipAddress); 159 | } 160 | 161 | /** 162 | * 判断字符串是否符合正则表达式 163 | * 164 | * @author : chenssy 165 | * @date : 2016年6月1日 下午12:43:05 166 | * 167 | * @param str 168 | * @param regex 169 | * @return 170 | */ 171 | public static boolean find(String str, String regex) { 172 | Pattern p = Pattern.compile(regex); 173 | Matcher m = p.matcher(str); 174 | boolean b = m.find(); 175 | return b; 176 | } 177 | 178 | /** 179 | * 判断输入的字符串是否符合Email格式. 180 | * @autor:chenssy 181 | * @date:2014年8月7日 182 | * 183 | * @param email 184 | * 传入的字符串 185 | * @return 符合Email格式返回true,否则返回false 186 | */ 187 | public static boolean isEmail(String email) { 188 | if (email == null || email.length() < 1 || email.length() > 256) { 189 | return false; 190 | } 191 | Pattern pattern = Pattern.compile("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"); 192 | return pattern.matcher(email).matches(); 193 | } 194 | 195 | /** 196 | * 判断输入的字符串是否为纯汉字 197 | * @autor:chenssy 198 | * @date:2014年8月7日 199 | * 200 | * @param value 201 | * 传入的字符串 202 | * @return 203 | */ 204 | public static boolean isChinese(String value) { 205 | Pattern pattern = Pattern.compile("[\u0391-\uFFE5]+$"); 206 | return pattern.matcher(value).matches(); 207 | } 208 | 209 | /** 210 | * 判断是否为浮点数,包括double和float 211 | * @autor:chenssy 212 | * @date:2014年8月7日 213 | * 214 | * @param value 215 | * 传入的字符串 216 | * @return 217 | */ 218 | public static boolean isDouble(String value) { 219 | Pattern pattern = Pattern.compile("^[-\\+]?\\d+\\.\\d+$"); 220 | return pattern.matcher(value).matches(); 221 | } 222 | 223 | /** 224 | * 判断是否为整数 225 | * @autor:chenssy 226 | * @date:2014年8月7日 227 | * 228 | * @param value 229 | * 传入的字符串 230 | * @retur 231 | */ 232 | public static boolean isInteger(String value) { 233 | Pattern pattern = Pattern.compile("^[-\\+]?[\\d]+$"); 234 | return pattern.matcher(value).matches(); 235 | } 236 | 237 | /** 238 | * 判断是否是字母 239 | * @param value 240 | * @return 241 | */ 242 | public static boolean checkChar(String value) { 243 | Pattern pattern = Pattern.compile("[a-z|A-Z]+"); 244 | return pattern.matcher(value).matches(); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/format/MoneyUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.format; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.RoundingMode; 5 | 6 | /** 7 | * 金钱处理工具类 8 | * 9 | * @Author:chenssy 10 | * @date:2014年8月7日 11 | */ 12 | public class MoneyUtils { 13 | 14 | /** 15 | * 汉语中数字大写 16 | */ 17 | private static final String[] CN_UPPER_NUMBER = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"}; 18 | 19 | /** 20 | * 汉语中货币单位大写 21 | */ 22 | private static final String[] CN_UPPER_MONEY_UNIT = {"分", "角", "元", "拾", "佰", "仟", "万", "拾", 23 | "佰", "仟", "亿", "拾", "佰", "仟", "兆", "拾", 24 | "佰", "仟"}; 25 | /** 26 | * 特殊字符:整 27 | */ 28 | private static final String CN_FULL = ""; 29 | 30 | /** 31 | * 特殊字符:负 32 | */ 33 | private static final String CN_NEGATIVE = "负"; 34 | /** 35 | * 零元整 36 | */ 37 | private static final String CN_ZERO_FULL = "零元整"; 38 | 39 | /** 40 | * 金额的精度,默认值为2 41 | */ 42 | private static final int MONEY_PRECISION = 2; 43 | 44 | /** 45 | * 人民币转换为大写,格式为:x万x千x百x十x元x角x分 46 | * 47 | * @param numberOfMoney 传入的金额 48 | * @return 49 | * @autor:chenssy 50 | * @date:2014年8月7日 51 | */ 52 | public static String number2CNMontray(String numberOfMoney) { 53 | return number2CNMontray(new BigDecimal(numberOfMoney)); 54 | } 55 | 56 | 57 | /** 58 | * 人民币转换为大写,格式为:x万x千x百x十x元x角x分 59 | * 60 | * @param numberOfMoney 传入的金额 61 | * @return 62 | * @autor:chenssy 63 | * @date:2014年8月7日 64 | */ 65 | public static String number2CNMontray(BigDecimal numberOfMoney) { 66 | StringBuffer sb = new StringBuffer(); 67 | int signum = numberOfMoney.signum(); 68 | // 零元整的情况 69 | if (signum == 0) { 70 | return CN_ZERO_FULL; 71 | } 72 | //这里会进行金额的四舍五入 73 | long number = numberOfMoney.movePointRight(MONEY_PRECISION).setScale(0, 4).abs().longValue(); 74 | // 得到小数点后两位值 75 | long scale = number % 100; 76 | int numUnit = 0; 77 | int numIndex = 0; 78 | boolean getZero = false; 79 | // 判断最后两位数,一共有四中情况:00 = 0, 01 = 1, 10, 11 80 | if (!(scale > 0)) { 81 | numIndex = 2; 82 | number = number / 100; 83 | getZero = true; 84 | } 85 | if ((scale > 0) && (!(scale % 10 > 0))) { 86 | numIndex = 1; 87 | number = number / 10; 88 | getZero = true; 89 | } 90 | int zeroSize = 0; 91 | while (true) { 92 | if (number <= 0) { 93 | break; 94 | } 95 | // 每次获取到最后一个数 96 | numUnit = (int) (number % 10); 97 | if (numUnit > 0) { 98 | if ((numIndex == 9) && (zeroSize >= 3)) { 99 | sb.insert(0, CN_UPPER_MONEY_UNIT[6]); 100 | } 101 | if ((numIndex == 13) && (zeroSize >= 3)) { 102 | sb.insert(0, CN_UPPER_MONEY_UNIT[10]); 103 | } 104 | sb.insert(0, CN_UPPER_MONEY_UNIT[numIndex]); 105 | sb.insert(0, CN_UPPER_NUMBER[numUnit]); 106 | getZero = false; 107 | zeroSize = 0; 108 | } else { 109 | ++zeroSize; 110 | if (!(getZero)) { 111 | sb.insert(0, CN_UPPER_NUMBER[numUnit]); 112 | } 113 | if (numIndex == 2) { 114 | if (number > 0) { 115 | sb.insert(0, CN_UPPER_MONEY_UNIT[numIndex]); 116 | } 117 | } else if (((numIndex - 2) % 4 == 0) && (number % 1000 > 0)) { 118 | sb.insert(0, CN_UPPER_MONEY_UNIT[numIndex]); 119 | } 120 | getZero = true; 121 | } 122 | // 让number每次都去掉最后一个数 123 | number = number / 10; 124 | ++numIndex; 125 | } 126 | // 如果signum == -1,则说明输入的数字为负数,就在最前面追加特殊字符:负 127 | if (signum == -1) { 128 | sb.insert(0, CN_NEGATIVE); 129 | } 130 | // 输入的数字小数点后两位为"00"的情况,则要在最后追加特殊字符:整 131 | if (!(scale > 0)) { 132 | sb.append(CN_FULL); 133 | } 134 | return sb.toString(); 135 | } 136 | 137 | /** 138 | * 将人民币转换为会计格式金额(xxxx,xxxx,xxxx.xx),保留两位小数 139 | * 140 | * @param money 待转换的金额 141 | * @return 142 | * @autor:chenssy 143 | * @date:2014年8月7日 144 | */ 145 | public static String accountantMoney(BigDecimal money) { 146 | return accountantMoney(money, 2, 1); 147 | } 148 | 149 | /** 150 | * 格式化金额,显示为xxx万元,xxx百万,xxx亿 151 | * 152 | * @param money 待处理的金额 153 | * @param scale 小数点后保留的位数 154 | * @param divisor 格式化值(10:十元、100:百元,1000千元,10000万元......) 155 | * @return 156 | * @autor:chenssy 157 | * @date:2014年8月7日 158 | */ 159 | public static String getFormatMoney(BigDecimal money, int scale, double divisor) { 160 | return formatMoney(money, scale, divisor) + getCellFormat(divisor); 161 | } 162 | 163 | /** 164 | * 获取会计格式的人民币(格式为:xxxx,xxxx,xxxx.xx) 165 | * 166 | * @param money 待处理的金额 167 | * @param scale 小数点后保留的位数 168 | * @param divisor 格式化值(10:十元、100:百元,1000千元,10000万元......) 169 | * @return 170 | * @autor:chenssy 171 | * @date:2014年8月7日 172 | */ 173 | public static String getAccountantMoney(BigDecimal money, int scale, double divisor) { 174 | return accountantMoney(money, scale, divisor) + getCellFormat(divisor); 175 | } 176 | 177 | /** 178 | * 将人民币转换为会计格式金额(xxxx,xxxx,xxxx.xx) 179 | * 180 | * @param money 待处理的金额 181 | * @param scale 小数点后保留的位数 182 | * @param divisor 格式化值 183 | * @return 184 | * @autor:chenssy 185 | * @date:2014年8月7日 186 | */ 187 | private static String accountantMoney(BigDecimal money, int scale, double divisor) { 188 | String disposeMoneyStr = formatMoney(money, scale, divisor); 189 | //小数点处理 190 | int dotPosition = disposeMoneyStr.indexOf("."); 191 | //小数点之前的字符串 192 | String exceptDotMoney = null; 193 | //小数点之后的字符串 194 | String dotMoney = null; 195 | if (dotPosition > 0) { 196 | exceptDotMoney = disposeMoneyStr.substring(0, dotPosition); 197 | dotMoney = disposeMoneyStr.substring(dotPosition); 198 | } else { 199 | exceptDotMoney = disposeMoneyStr; 200 | } 201 | //负数处理 202 | int negativePosition = exceptDotMoney.indexOf("-"); 203 | if (negativePosition == 0) { 204 | exceptDotMoney = exceptDotMoney.substring(1); 205 | } 206 | StringBuffer reverseExceptDotMoney = new StringBuffer(exceptDotMoney); 207 | reverseExceptDotMoney.reverse();//字符串倒转 208 | char[] moneyChar = reverseExceptDotMoney.toString().toCharArray(); 209 | //返回值 210 | StringBuffer returnMeony = new StringBuffer(); 211 | for (int i = 0; i < moneyChar.length; i++) { 212 | if (i != 0 && i % 3 == 0) { 213 | //每隔3位加',' 214 | returnMeony.append(","); 215 | } 216 | returnMeony.append(moneyChar[i]); 217 | } 218 | returnMeony.reverse();//字符串倒转 219 | if (dotPosition > 0) { 220 | returnMeony.append(dotMoney); 221 | } 222 | if (negativePosition == 0) { 223 | return "-" + returnMeony.toString(); 224 | } else { 225 | return returnMeony.toString(); 226 | } 227 | } 228 | 229 | /** 230 | * 格式化金额,显示为xxx万元,xxx百万,xxx亿 231 | * 232 | * @param money 待处理的金额 233 | * @param scale 小数点后保留的位数 234 | * @param divisor 格式化值 235 | * @return 236 | * @autor:chenssy 237 | * @date:2014年8月7日 238 | */ 239 | private static String formatMoney(BigDecimal money, int scale, double divisor) { 240 | if (divisor == 0) { 241 | return "0.00"; 242 | } 243 | if (scale < 0) { 244 | return "0.00"; 245 | } 246 | BigDecimal divisorBD = new BigDecimal(divisor); 247 | return money.divide(divisorBD, scale, RoundingMode.HALF_UP).toString(); 248 | } 249 | 250 | private static String getCellFormat(double divisor) { 251 | String str = String.valueOf(divisor); 252 | int len = str.substring(0, str.indexOf(".")).length(); 253 | String cell = ""; 254 | switch (len) { 255 | case 1: 256 | cell = "元"; 257 | break; 258 | case 2: 259 | cell = "十元"; 260 | break; 261 | case 3: 262 | cell = "百元"; 263 | break; 264 | case 4: 265 | cell = "千元"; 266 | break; 267 | case 5: 268 | cell = "万元"; 269 | break; 270 | case 6: 271 | cell = "十万元"; 272 | break; 273 | case 7: 274 | cell = "百万元"; 275 | break; 276 | case 8: 277 | cell = "千万元"; 278 | break; 279 | case 9: 280 | cell = "亿元"; 281 | break; 282 | case 10: 283 | cell = "十亿元"; 284 | break; 285 | } 286 | return cell; 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/file/CompressUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.file; 2 | 3 | import java.io.*; 4 | import java.nio.channels.FileChannel; 5 | import java.nio.channels.FileLock; 6 | import java.util.zip.*; 7 | 8 | /** 9 | * 对文件、输出流提供压缩、解压操作的工具类 10 | * 11 | * @author xuan 12 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午9:51:01 $ 13 | */ 14 | public abstract class CompressUtils { 15 | 16 | /** 17 | * 压缩处理类,客户端通过实现此方法来将数据写入输出流。 18 | */ 19 | interface CompressHandler { 20 | 21 | /** 22 | * 对输出流进行处理的方法,可以将需要进行压缩的数据写入该输出流中。 23 | *

24 | * {@link OutputStream#close()} 方法可以不调用,因为在工具方法中会进行关闭操作。 25 | * 26 | * @param out 输出流 27 | * @throws IOException 发生 I/O 错误时应当抛出此异常 28 | */ 29 | void handle(OutputStream out) throws IOException; 30 | } 31 | 32 | private static final int BUFFER_SIZE = 8192; 33 | 34 | /** 35 | * 以 GZip 方式对输出流进行压缩后保存到文件。注意:如果目标文件已经存在,则会覆盖已有文件的内容。 36 | *

37 | * 以下是两个使用的例子: 38 | *

39 | *

 40 |      * CompressUtils.gzCompress("foo.txt", new CompressHandler() {
 41 |      *     @Override
 42 |      *     public void handle(OutputStream out) throws IOException {
 43 |      *         out.write("This is a test.".getBytes());
 44 |      *     }
 45 |      * });
 46 |      * 
47 | *

48 | *

 49 |      * CompressUtils.gzCompress("bar.txt", new CompressHandler() {
 50 |      *     @Override
 51 |      *     public void handle(OutputStream out) throws IOException {
 52 |      *         BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
 53 |      *         writer.write("This is a test.");
 54 |      *         writer.flush(); // 刷新缓冲
 55 |      *     }
 56 |      * });
 57 |      * 
58 | * 59 | * @param dstFile 目标文件,即压缩后的文件 60 | * @param handler 压缩输出流处理类 61 | * @throws IOException 当压缩过程中发生错误等情况下抛出此异常 62 | */ 63 | public static void gzCompress(String dstFile, CompressHandler handler) throws IOException { 64 | if (!dstFile.endsWith(".gz")) { 65 | dstFile = dstFile + ".gz"; 66 | } 67 | 68 | GZIPOutputStream gzos = null; 69 | FileChannel channel = null; 70 | FileLock lock = null; 71 | 72 | try { 73 | FileOutputStream fos = new FileOutputStream(dstFile); 74 | channel = fos.getChannel(); 75 | lock = channel.lock(); 76 | 77 | gzos = new GZIPOutputStream(fos); 78 | handler.handle(gzos); 79 | } catch (IOException e) { 80 | throw new IOException("Error occurred while compressing stream into [" + dstFile + "].", e); 81 | } finally { 82 | clean(null, gzos, lock, channel); 83 | } 84 | } 85 | 86 | /** 87 | * 对指定的文件以 GZip 方式进行压缩。注意:如果目标文件已经存在,则会覆盖已有文件的内容。 88 | * 89 | * @param srcFile 源文件,即待压缩的文件 90 | * @param dstFile 目标文件,即压缩后的文件 91 | * @throws IOException 当源文件不存在、压缩过程中发生错误等情况下抛出此异常 92 | */ 93 | public static void gzCompress(String srcFile, String dstFile) throws IOException { 94 | File file2Compress = new File(srcFile); 95 | if (!file2Compress.exists()) { 96 | throw new IOException("The file to compress named [" + srcFile + "] does not exist."); 97 | } 98 | 99 | if (!dstFile.endsWith(".gz")) { 100 | dstFile = dstFile + ".gz"; 101 | } 102 | 103 | FileInputStream fis = null; 104 | GZIPOutputStream gzos = null; 105 | FileChannel channel = null; 106 | FileLock lock = null; 107 | 108 | try { 109 | FileOutputStream fos = new FileOutputStream(dstFile); 110 | 111 | // 取得文件锁,保证在写文件过程中不会被其他进程读到不完整的数据 112 | channel = fos.getChannel(); 113 | lock = channel.lock(); 114 | 115 | fis = new FileInputStream(srcFile); 116 | gzos = new GZIPOutputStream(fos); 117 | 118 | byte[] buffer = new byte[BUFFER_SIZE]; 119 | for (int len = 0; (len = fis.read(buffer)) != -1; ) { 120 | gzos.write(buffer, 0, len); 121 | } 122 | } catch (IOException e) { 123 | throw new IOException("Error occurred while compressing [" + srcFile + "] into [" + dstFile + "].", e); 124 | } finally { 125 | clean(fis, gzos, lock, channel); 126 | } 127 | } 128 | 129 | /** 130 | * 对指定的文件以 GZip 方式进行解压缩。注意:如果目标文件已经存在,则会覆盖已有文件的内容。 131 | * 132 | * @param srcFile 源文件,即待解压的文件 133 | * @param dstFile 目标文件,即解压后的文件 134 | * @throws IOException 当源文件不存在、解压缩过程中发生错误等情况下抛出此异常 135 | */ 136 | public static void gzDecompress(String srcFile, String dstFile) throws IOException { 137 | File compressedFile = new File(srcFile); 138 | if (!compressedFile.exists()) { 139 | throw new IOException("The file to decompress named [" + srcFile + "] does not exist."); 140 | } 141 | 142 | GZIPInputStream gzis = null; 143 | FileOutputStream fos = null; 144 | 145 | try { 146 | gzis = new GZIPInputStream(new FileInputStream(srcFile)); 147 | fos = new FileOutputStream(dstFile); 148 | 149 | byte[] buffer = new byte[BUFFER_SIZE]; 150 | for (int len = 0; (len = gzis.read(buffer)) != -1; ) { 151 | fos.write(buffer, 0, len); 152 | } 153 | } catch (IOException e) { 154 | throw new IOException("Error occurred while decompressing [" + srcFile + "] into [" + dstFile + "].", e); 155 | } finally { 156 | clean(gzis, fos); 157 | } 158 | } 159 | 160 | /** 161 | * 对指定的文件以 Zip 方式进行压缩,目前只支持单个文件,不支持对目录进行压缩操作。 162 | *

163 | * 注意:如果目标文件已经存在,则会覆盖已有文件的内容。 164 | * 165 | * @param srcFile 源文件,即待压缩的文件 166 | * @param dstFile 目标文件,即压缩后的文件 167 | * @throws IOException 当源文件不存在、压缩过程中发生错误等情况下抛出此异常 168 | */ 169 | public static void zipCompress(String srcFile, String dstFile) throws IOException { 170 | File file2Compress = new File(srcFile); 171 | if (!file2Compress.exists()) { 172 | throw new IOException("The file to compress named [" + srcFile + "] does not exist."); 173 | } 174 | 175 | if (!dstFile.endsWith(".zip")) { 176 | dstFile = dstFile + ".zip"; 177 | } 178 | 179 | FileInputStream fis = null; 180 | ZipOutputStream zos = null; 181 | FileChannel channel = null; 182 | FileLock lock = null; 183 | 184 | try { 185 | FileOutputStream fos = new FileOutputStream(dstFile); 186 | channel = fos.getChannel(); 187 | lock = channel.lock(); 188 | 189 | fis = new FileInputStream(srcFile); 190 | zos = new ZipOutputStream(fos); 191 | 192 | ZipEntry entry = new ZipEntry(file2Compress.getName()); 193 | entry.setCompressedSize(file2Compress.length()); 194 | entry.setTime(file2Compress.lastModified()); 195 | zos.putNextEntry(entry); 196 | 197 | byte[] buffer = new byte[BUFFER_SIZE]; 198 | for (int len = 0; (len = fis.read(buffer)) != -1; ) { 199 | zos.write(buffer, 0, len); 200 | } 201 | } catch (IOException e) { 202 | throw new IOException("Error occurred while compressing [" + srcFile + "] into [" + dstFile + "]."); 203 | } finally { 204 | clean(fis, zos, lock, channel); 205 | } 206 | } 207 | 208 | /** 209 | * 对指定的文件以 Zip 方式进行解压缩。目前只支持单个文件,不支持对目录进行解压操作。 210 | *

211 | * 注意:如果目标文件已经存在,则会覆盖已有文件的内容。 212 | * 213 | * @param srcFile 源文件,即待解压的文件 214 | * @param dstFile 目标文件,即解压后的文件 215 | * @throws IOException 当源文件不存在、解压缩过程中发生错误等情况下抛出此异常 216 | */ 217 | public static void zipDecompress(String srcFile, String dstFile) throws IOException { 218 | File compressedFile = new File(srcFile); 219 | if (!compressedFile.exists()) { 220 | throw new IOException("The file to decompress named [" + srcFile + "] does not exist."); 221 | } 222 | 223 | ZipInputStream zis = null; 224 | FileOutputStream fos = null; 225 | 226 | try { 227 | zis = new ZipInputStream(new FileInputStream(srcFile)); 228 | fos = new FileOutputStream(dstFile); 229 | 230 | ZipEntry entry = zis.getNextEntry(); 231 | if (entry == null) { 232 | throw new IOException("The file to decompress named [" + srcFile + "] has no zip entry."); 233 | } 234 | 235 | byte[] buffer = new byte[BUFFER_SIZE]; 236 | for (int len = 0; (len = zis.read(buffer)) != -1; ) { 237 | fos.write(buffer, 0, len); 238 | } 239 | } catch (IOException e) { 240 | throw new IOException("Error occurred while decompressing [" + srcFile + "] into [" + dstFile + "]."); 241 | } finally { 242 | clean(zis, fos); 243 | } 244 | } 245 | 246 | /** 247 | * 释放资源,关闭输入输出流。 248 | */ 249 | private static void clean(InputStream in, OutputStream out) throws IOException { 250 | FileIoUtil.close(in); 251 | FileIoUtil.close(out); 252 | } 253 | 254 | /** 255 | * 释放资源,包括关闭输入输出流、关闭文件通道、释放文件锁。 256 | */ 257 | private static void clean(InputStream in, OutputStream out, FileLock lock, FileChannel channel) throws IOException { 258 | FileIoUtil.close(in); 259 | 260 | // 释放文件锁 261 | if (lock != null) { 262 | lock.release(); 263 | } 264 | 265 | // 在关闭压缩输出流之后再关闭通道,如果先关闭通道会导致 压缩文件的格式错误 266 | FileIoUtil.close(out); 267 | if (channel != null) { 268 | channel.close(); 269 | } 270 | } 271 | 272 | } 273 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/validate/Validators.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.validate; 2 | 3 | import me.xueyao.date.DateTools; 4 | 5 | import java.util.Collection; 6 | import java.util.Map; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * 一些基本的字符串,集合,数字等校验工具 11 | * 12 | * @author xuan 13 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午9:44:21 $ 14 | */ 15 | public abstract class Validators { 16 | 17 | /** 18 | * 简体中文的正则表达式。 19 | */ 20 | private static final String REGEX_SIMPLE_CHINESE = "^[\u4E00-\u9FA5]+$"; 21 | 22 | /** 23 | * 字母数字的正则表达式。 24 | */ 25 | private static final String REGEX_ALPHANUMERIC = "[a-zA-Z0-9]+"; 26 | 27 | /** 28 | * 整数或浮点数的正则表达式。 29 | */ 30 | private static final String REGEX_NUMERIC = "(\\+|-){0,1}(\\d+)([.]?)(\\d*)"; 31 | 32 | /** 33 | * 身份证号码的正则表达式。 34 | */ 35 | private static final String REGEX_ID_CARD = "(\\d{14}|\\d{17})(\\d|x|X)"; 36 | 37 | /** 38 | * 电子邮箱的正则表达式。 39 | */ 40 | private static final String REGEX_EMAIL = ".+@.+\\.[a-z]+"; 41 | 42 | /** 43 | * 判断字符串是否只包含字母和数字. 44 | * 45 | * @param str 字符串 46 | * @return 如果字符串只包含字母和数字, 则返回 true, 否则返回 false. 47 | */ 48 | public static boolean isAlphanumeric(String str) { 49 | return isRegexMatch(str, REGEX_ALPHANUMERIC); 50 | } 51 | 52 | /** 53 | *

54 | * Checks if a String is whitespace, empty ("") or null. 55 | *

56 | *

57 | *

 58 |      *   Validators.isBlank(null)                = true
 59 |      *   Validators.isBlank("")        = true
 60 |      *   Validators.isBlank(" ")       = true
 61 |      *   Validators.isBlank("bob")     = false
 62 |      *   Validators.isBlank("  bob  ") = false
 63 |      * 
64 | * 65 | * @param str the String to check, may be null 66 | * @return true if the String is null, empty or whitespace 67 | */ 68 | public static boolean isBlank(String str) { 69 | int strLen; 70 | if (str == null || (strLen = str.length()) == 0) { 71 | return true; 72 | } 73 | for (int i = 0; i < strLen; i++) { 74 | if ((Character.isWhitespace(str.charAt(i)) == false)) { 75 | return false; 76 | } 77 | } 78 | return true; 79 | } 80 | 81 | /** 82 | * 是否是合法的日期字符串(类似格式:2017-03-01是合法的) 83 | *

84 | *

 85 |      *   Validators.isBlank("2017-03-01")       = true
 86 |      *   Validators.isBlank("2017-0301")        = false
 87 |      * 
88 | * 89 | * @param str 日期字符串 90 | * @return 是true,否则false 91 | */ 92 | public static boolean isDate(String str) { 93 | if (isEmpty(str) || str.length() > 10) { 94 | return false; 95 | } 96 | 97 | String[] items = str.split("-"); 98 | 99 | if (items.length != 3) { 100 | return false; 101 | } 102 | 103 | if (!isNumber(items[0], 1900, 9999) || !isNumber(items[1], 1, 12)) { 104 | return false; 105 | } 106 | 107 | int year = Integer.parseInt(items[0]); 108 | int month = Integer.parseInt(items[1]); 109 | 110 | return isNumber(items[2], 1, DateTools.getMaxDayOfMonth(year, month - 1)); 111 | } 112 | 113 | /** 114 | * 是否是合法的日期时间字符串 115 | *

116 | *

117 |      *   Validators.isDateTime("2017-03-01 12:03:01")       = true
118 |      *   Validators.isDateTime("2017-03-01 12:0301")        = false
119 |      * 
120 | * 121 | * @param str 日期时间字符串 122 | * @return 是true,否则false 123 | */ 124 | public static boolean isDateTime(String str) { 125 | if (isEmpty(str) || str.length() > 20) { 126 | return false; 127 | } 128 | 129 | String[] items = str.split(" "); 130 | 131 | if (items.length != 2) { 132 | return false; 133 | } 134 | 135 | return isDate(items[0]) && isTime(items[1]); 136 | } 137 | 138 | /** 139 | * 判断字符串是否是合法的电子邮箱地址. 140 | * 141 | * @param str 字符串 142 | * @return 是true,否则false 143 | */ 144 | public static boolean isEmail(String str) { 145 | return isRegexMatch(str, REGEX_EMAIL); 146 | } 147 | 148 | /** 149 | * 当数组为null, 或者长度为0, 或者长度为1且元素的值为null时返回 true. 150 | * 151 | * @param args 152 | * @return true/false 153 | */ 154 | public static boolean isEmpty(Object[] args) { 155 | return args == null || args.length == 0 || (args.length == 1 && args[0] == null); 156 | } 157 | 158 | /** 159 | * 字符串是否为Empty,null和空格都算是Empty 160 | * 161 | * @param str 字符串 162 | * @return true/false 163 | */ 164 | public static boolean isEmpty(String str) { 165 | return str == null || str.trim().length() == 0; 166 | } 167 | 168 | /** 169 | * 判断集合是否为空。 170 | * 171 | * @param 集合泛型 172 | * @param collection 集合对象 173 | * @return 当集合对象为 null 或者长度为零时返回 true,否则返回 false。 174 | */ 175 | public static boolean isEmpty(Collection collection) { 176 | return collection == null || collection.isEmpty(); 177 | } 178 | 179 | /** 180 | * 判断Map是否为空 181 | * 182 | * @param map Map对象 183 | * @param 184 | * @param 185 | * @return 当Map对象为 null 或者元素为空是返回 true,否则返回 false。 186 | */ 187 | public static boolean isEmptyMap(Map map) { 188 | return map == null || map.isEmpty(); 189 | } 190 | 191 | /** 192 | *

193 | * Validating for ID card number. 194 | *

195 | * 196 | * @param str string to be validated 197 | * @return If the str is valid ID card number return true, otherwise return false. 198 | */ 199 | public static boolean isIdCardNumber(String str) { 200 | // 15位或18数字, 14数字加x(X)字符或17数字加x(X)字符才是合法的 201 | return isRegexMatch(str, REGEX_ID_CARD); 202 | } 203 | 204 | /** 205 | * 是否为数字的字符串。 206 | * 207 | * @param str 字符串 208 | * @return true/false 209 | */ 210 | public static boolean isNumber(String str) { 211 | if (isEmpty(str)) { 212 | return false; 213 | } 214 | 215 | for (int i = 0; i < str.length(); i++) { 216 | if (str.charAt(i) > '9' || str.charAt(i) < '0') { 217 | return false; 218 | } 219 | } 220 | return true; 221 | } 222 | 223 | /** 224 | * 是否是固定范围内的数字的字符串 225 | * 226 | * @param str 227 | * @param min 228 | * @param max 229 | * @return true/false 230 | */ 231 | public static boolean isNumber(String str, int min, int max) { 232 | if (!isNumber(str)) { 233 | return false; 234 | } 235 | 236 | int number = Integer.parseInt(str); 237 | return number >= min && number <= max; 238 | } 239 | 240 | /** 241 | * 判断字符是否为整数或浮点数.
242 | * 243 | * @param str 字符串 244 | * @return 若为整数或浮点数则返回 true, 否则返回 false 245 | */ 246 | public static boolean isNumeric(String str) { 247 | return isRegexMatch(str, REGEX_NUMERIC); 248 | } 249 | 250 | /** 251 | * 判断字符是否为符合精度要求的整数或浮点数。 252 | * 253 | * @param str 字符串 254 | * @param fractionNum 小数部分的最多允许的位数 255 | * @return 若为整数或浮点数则返回 true, 否则返回 false 256 | */ 257 | public static boolean isNumeric(String str, int fractionNum) { 258 | if (isEmpty(str)) { 259 | return false; 260 | } 261 | 262 | // 整数或浮点数 263 | String regex = "(\\+|-){0,1}(\\d+)([.]?)(\\d{0," + fractionNum + "})"; 264 | return Pattern.matches(regex, str); 265 | } 266 | 267 | /** 268 | * 判断是否是合法的邮编 269 | * 270 | * @param str 字符串 271 | * @return true/false 272 | */ 273 | public static boolean isPostcode(String str) { 274 | if (isEmpty(str)) { 275 | return false; 276 | } 277 | 278 | if (str.length() != 6 || !Validators.isNumber(str)) { 279 | return false; 280 | } 281 | 282 | return true; 283 | } 284 | 285 | /** 286 | * 判断是否是固定长度范围内的字符串 287 | * 288 | * @param str 289 | * @param minLength 290 | * @param maxLength 291 | * @return true/false 292 | */ 293 | public static boolean isString(String str, int minLength, int maxLength) { 294 | if (str == null) { 295 | return false; 296 | } 297 | 298 | if (minLength < 0) { 299 | return str.length() <= maxLength; 300 | } else if (maxLength < 0) { 301 | return str.length() >= minLength; 302 | } else { 303 | return str.length() >= minLength && str.length() <= maxLength; 304 | } 305 | } 306 | 307 | /** 308 | * 判断是否是合法的时间字符串。 309 | * 310 | * @param str 字符串 311 | * @return true/false 312 | */ 313 | public static boolean isTime(String str) { 314 | if (isEmpty(str) || str.length() > 8) { 315 | return false; 316 | } 317 | 318 | String[] items = str.split(":"); 319 | 320 | if (items.length != 2 && items.length != 3) { 321 | return false; 322 | } 323 | 324 | for (int i = 0; i < items.length; i++) { 325 | if (items[i].length() != 2 && items[i].length() != 1) { 326 | return false; 327 | } 328 | } 329 | 330 | return !(!isNumber(items[0], 0, 23) || !isNumber(items[1], 0, 59) || (items.length == 3 && !isNumber(items[2], 331 | 0, 59))); 332 | } 333 | 334 | /** 335 | * 是否是简体中文字符串。 336 | * 337 | * @param str 字符串 338 | * @return true/false 339 | */ 340 | public static boolean isSimpleChinese(String str) { 341 | return isRegexMatch(str, REGEX_SIMPLE_CHINESE); 342 | } 343 | 344 | /** 345 | * 判断字符串是否匹配了正则表达式。 346 | * 347 | * @param str 字符串 348 | * @param regex 正则表达式 349 | * @return true/false 350 | */ 351 | public static boolean isRegexMatch(String str, String regex) { 352 | return str != null && str.matches(regex); 353 | } 354 | 355 | } 356 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/encryption/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.encryption; 2 | 3 | import me.xueyao.StringUtils; 4 | 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | /** 9 | * 加解密工具类 10 | * 11 | * @author xuan 12 | * @version $Revision: 1.0 $, $Date: 2012-11-22 上午10:10:59 $ 13 | */ 14 | public abstract class SecurityUtils { 15 | 16 | private static final char[] chs = {'L', 'K', 'J', '4', 'D', 'G', 'F', 'V', 'R', 'T', 'Y', 'B', 'N', 'U', 'P', 'W', 17 | '3', 'E', '5', 'H', 'M', '7', 'Q', '9', 'S', 'A', 'Z', 'X', '8', 'C', '6', '2'}; 18 | 19 | /** 20 | * 自身混淆加密,最多只能加密 30 个字节长度的字符串。 21 | * 22 | *

23 | * 对同一个字符串,加密后的密文可能是不相同的,所以在判断密码是否相等时,不能采用密文进行比对,必须采用明文比对。 24 | *

25 | * 26 | * @param source 源字符串 27 | * @return 加密后字符串 28 | * @see {@link #decodeBySelf(String)} 29 | */ 30 | public static String encodeBySelf(String source) { 31 | if (source == null) { 32 | throw new NullPointerException("source can't be null"); 33 | } 34 | 35 | if (source.length() > 30) { 36 | throw new IllegalArgumentException("the length of source must be less than 31, actual was " 37 | + source.length()); 38 | } 39 | 40 | String plainText = source; 41 | byte[] plainTextBytes = plainText.getBytes(); 42 | 43 | byte[] encodedBytes1 = new byte[30]; 44 | byte[] encodedBytes2 = new byte[30]; 45 | 46 | int n1 = 0, n2 = 0; 47 | for (int i = 0; i < plainTextBytes.length; i++) { 48 | // 奇数位 49 | if ((i + 1) % 2 != 0) { 50 | encodedBytes1[n1++] = (byte) get32Hi(plainTextBytes[i] * 4); 51 | encodedBytes1[n1++] = (byte) get32Low(plainTextBytes[i] * 4); 52 | } else { // 偶数位 53 | encodedBytes2[n2++] = (byte) get32Hi(plainTextBytes[i] * 4); 54 | encodedBytes2[n2++] = (byte) get32Low(plainTextBytes[i] * 4); 55 | } 56 | } 57 | 58 | while (n1 < 30) { 59 | encodedBytes1[n1++] = (byte) getRandom(32); 60 | } 61 | 62 | while (n2 < 30) { 63 | encodedBytes2[n2++] = (byte) getRandom(32); 64 | } 65 | 66 | int pos1 = getRandom(plainTextBytes.length); 67 | int pos2 = getRandom(plainTextBytes.length); 68 | sort(encodedBytes1, pos1); 69 | sort(encodedBytes2, pos2); 70 | int check = (sumSqual(encodedBytes1) + sumSqual(encodedBytes2)) % 32; 71 | 72 | byte[] encodedArray = new byte[64]; 73 | encodedArray[0] = (byte) pos1; 74 | encodedArray[1] = (byte) pos2; 75 | System.arraycopy(encodedBytes1, 0, encodedArray, 2, encodedBytes1.length); 76 | System.arraycopy(encodedBytes2, 0, encodedArray, 2 + encodedBytes1.length, encodedBytes2.length); 77 | encodedArray[encodedArray.length - 2] = (byte) plainText.length(); 78 | encodedArray[encodedArray.length - 1] = (byte) check; 79 | byte[] ps = new byte[encodedArray.length]; 80 | 81 | for (int i = 0; i < encodedArray.length; i++) { 82 | ps[i] = (byte) chs[encodedArray[i]]; 83 | } 84 | 85 | return new String(ps); 86 | } 87 | 88 | /** 89 | * 自身混淆解密。如果不是合法的加密串(长度不是64个字节),会直接返回原字符串。 90 | * 91 | * @param str 加密的字符串 92 | * @return 解密后字符串 93 | * @see {@link #encodeBySelf(String)} 94 | */ 95 | public static String decodeBySelf(String str) { 96 | // 如果不是合法的加密串,则直接返回 97 | if (str == null || str.length() != 64) { 98 | return str; 99 | } 100 | 101 | byte[] bb = new byte[str.length()]; 102 | byte[] sb = str.getBytes(); 103 | 104 | for (int i = 0; i < sb.length; i++) { 105 | for (int j = 0; j < 32; j++) { 106 | if (((byte) chs[j]) == sb[i]) { 107 | bb[i] = (byte) j; 108 | break; 109 | } 110 | } 111 | } 112 | 113 | int sl = bb[bb.length - 2]; 114 | int p1 = bb[0]; 115 | int p2 = bb[1]; 116 | 117 | byte[] bb1 = new byte[30]; 118 | byte[] bb2 = new byte[30]; 119 | 120 | int bb2l; 121 | if (sl % 2 == 0) { 122 | bb2l = sl; 123 | } else { 124 | bb2l = sl - 1; 125 | } 126 | 127 | System.arraycopy(bb, 2, bb1, 0, bb1.length); 128 | System.arraycopy(bb, 2 + bb1.length, bb2, 0, bb2.length); 129 | 130 | unsort(bb1, p1); 131 | unsort(bb2, p2); 132 | byte[] oldb = new byte[sl]; 133 | for (int i = 0; i < sl; i += 2) { 134 | oldb[i] = (byte) (getIntFrom32(bb1[i], bb1[i + 1]) / 4); 135 | if (i + 1 < bb2l) { 136 | oldb[i + 1] = (byte) (getIntFrom32(bb2[i], bb2[i + 1]) / 4); 137 | } 138 | } 139 | 140 | return new String(oldb); 141 | } 142 | 143 | /** 144 | * 使用 SHA1 加密。 145 | * 146 | * @param str 源字符串 147 | * @return 加密后字符串 148 | */ 149 | public static String encodeBySHA1(String str) { 150 | try { 151 | MessageDigest md = MessageDigest.getInstance("SHA-1"); 152 | md.update(str.getBytes()); 153 | return StringUtils.toHexString(md.digest()); 154 | } catch (NoSuchAlgorithmException e) { 155 | throw new RuntimeException("Could not encodeBySHA1", e); 156 | } 157 | } 158 | 159 | /** 160 | * 使用 MD5 对字符串加密。 161 | * 162 | * @param str 源字符串 163 | * @return 加密后字符串 164 | */ 165 | public static String encodeByMD5(String str) { 166 | try { 167 | MessageDigest md = MessageDigest.getInstance("MD5"); 168 | md.update(str.getBytes()); 169 | return StringUtils.toHexString(md.digest()); 170 | } catch (NoSuchAlgorithmException e) { 171 | throw new RuntimeException("Could not encodeByMD5", e); 172 | } 173 | } 174 | 175 | /** 176 | * 使用 MD5 对字节数组加密。 177 | * 178 | * @param bytes 源字符 byte 数组 179 | * @return 加密后字符串 180 | */ 181 | public static String encodeByMD5(byte[] bytes) { 182 | try { 183 | MessageDigest md = MessageDigest.getInstance("MD5"); 184 | md.update(bytes); 185 | return StringUtils.toHexString(md.digest()); 186 | } catch (NoSuchAlgorithmException e) { 187 | throw new RuntimeException("Could not encodeByMD5", e); 188 | } 189 | } 190 | 191 | /** 192 | * 使用36进制解码。 193 | * 194 | * @param str 编码的字符串 195 | * @return 解码后字符串 196 | * @see {@link #encodeBy36Radix(String)} 197 | */ 198 | public static String decodeBy36Radix(String str) { 199 | int length = str.length(); 200 | 201 | byte[] bytes = null; 202 | if (length % 11 == 0) { 203 | bytes = new byte[length / 11 * 7]; 204 | } else { 205 | bytes = new byte[(length / 11 + 1) * 7]; 206 | } 207 | 208 | int index = 0; 209 | int offset = 0; 210 | do { 211 | String sub = null; 212 | if (index + 11 < length) { 213 | sub = str.substring(index, index + 11); 214 | } else { 215 | sub = str.substring(index); 216 | } 217 | 218 | long l = Long.parseLong(sub, 36); 219 | for (int i = 0; i < 7; i++) { 220 | byte b = (byte) (l >> ((6 - i) * 8)); 221 | 222 | if (b != 0) { 223 | bytes[offset++] = b; 224 | } 225 | } 226 | index += 11; 227 | } 228 | while (index < length); 229 | 230 | return new String(bytes, 0, offset); 231 | } 232 | 233 | /** 234 | * 使用36进制进行编码。 235 | * 236 | * @param str 源字符串 237 | * @return 编码后字符串 238 | * @see {@link #decodeBy36Radix(String)} 239 | */ 240 | public static String encodeBy36Radix(String str) { 241 | byte[] bytes = str.getBytes(); 242 | int index = 0; 243 | StringBuilder StringBuilder = new StringBuilder(); 244 | boolean isSeven = true; 245 | 246 | do { 247 | byte[] longBytes = new byte[8]; 248 | 249 | if (index + 7 < bytes.length) { 250 | System.arraycopy(bytes, index, longBytes, 1, 7); 251 | } else { 252 | int i = bytes.length - index; 253 | System.arraycopy(bytes, index, longBytes, 8 - i, i); 254 | isSeven = false; 255 | } 256 | 257 | long longval = toLong(longBytes); 258 | 259 | if (isSeven) { 260 | StringBuilder.append(StringUtils.enoughZero(Long.toString(longval, 36), 11)); 261 | } else { 262 | StringBuilder.append(Long.toString(longval, 36)); 263 | } 264 | 265 | index += 7; 266 | } 267 | while (index < bytes.length); 268 | 269 | return StringBuilder.toString(); 270 | } 271 | 272 | private static long toLong(byte[] bytes) { 273 | return ((((long) bytes[0] & 0xff) << 56) | (((long) bytes[1] & 0xff) << 48) | (((long) bytes[2] & 0xff) << 40) 274 | | (((long) bytes[3] & 0xff) << 32) | (((long) bytes[4] & 0xff) << 24) 275 | | (((long) bytes[5] & 0xff) << 16) | (((long) bytes[6] & 0xff) << 8) | (((long) bytes[7] & 0xff) << 0)); 276 | } 277 | 278 | private static int sumSqual(byte[] b) { 279 | int sum = 0; 280 | for (int i = 0; i < b.length; i++) { 281 | sum += (int) Math.pow(b[i], 2); 282 | } 283 | return sum; 284 | } 285 | 286 | private static int getRandom(int max) { 287 | return (int) (Math.random() * max); 288 | } 289 | 290 | private static void sort(byte[] b, int pos) { 291 | byte[] tmp = new byte[pos]; 292 | System.arraycopy(b, 0, tmp, 0, pos); 293 | System.arraycopy(b, pos, b, 0, b.length - pos); 294 | System.arraycopy(tmp, 0, b, b.length - pos, pos); 295 | } 296 | 297 | private static void unsort(byte[] b, int pos) { 298 | byte[] tmp = new byte[pos]; 299 | System.arraycopy(b, b.length - pos, tmp, 0, pos); 300 | System.arraycopy(b, 0, b, pos, b.length - pos); 301 | System.arraycopy(tmp, 0, b, 0, pos); 302 | } 303 | 304 | private static int get32Low(int num) { 305 | return num % 32; 306 | } 307 | 308 | private static int get32Hi(int num) { 309 | return num / 32; 310 | } 311 | 312 | private static int getIntFrom32(int hi, int low) { 313 | return hi * 32 + low; 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/map/Geohash.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.map; 2 | 3 | 4 | import me.xueyao.http.HttpRequestUtils; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * 地图计算 10 | * @author simonxue 11 | */ 12 | public class Geohash { 13 | 14 | public final static String TENCENT_MAP_URL = "https://apis.map.qq.com/ws/place/v1/suggestion"; 15 | 16 | public final static String TENCENT_MAP_CODE = "腾讯地图提供的key"; 17 | 18 | private static int numbits = 6 * 5; 19 | final static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', 20 | '9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 21 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; 22 | 23 | final static HashMap lookup = new HashMap(); 24 | 25 | static { 26 | int i = 0; 27 | for (char c : digits) { 28 | lookup.put(c, i++); 29 | } 30 | } 31 | 32 | public double[] decode(String geohash) { 33 | StringBuilder buffer = new StringBuilder(); 34 | for (char c : geohash.toCharArray()) { 35 | 36 | int i = lookup.get(c) + 32; 37 | buffer.append(Integer.toString(i, 2).substring(1)); 38 | } 39 | 40 | BitSet lonset = new BitSet(); 41 | BitSet latset = new BitSet(); 42 | 43 | // even bits 44 | int j = 0; 45 | for (int i = 0; i < numbits * 2; i += 2) { 46 | boolean isSet = false; 47 | if (i < buffer.length()) { 48 | isSet = buffer.charAt(i) == '1'; 49 | } 50 | lonset.set(j++, isSet); 51 | } 52 | 53 | // odd bits 54 | j = 0; 55 | for (int i = 1; i < numbits * 2; i += 2) { 56 | boolean isSet = false; 57 | if (i < buffer.length()) { 58 | isSet = buffer.charAt(i) == '1'; 59 | } 60 | latset.set(j++, isSet); 61 | } 62 | 63 | double lon = decode(lonset, -180, 180); 64 | double lat = decode(latset, -90, 90); 65 | 66 | return new double[]{lat, lon}; 67 | } 68 | 69 | private double decode(BitSet bs, double floor, double ceiling) { 70 | double mid = 0; 71 | for (int i = 0; i < bs.length(); i++) { 72 | mid = (floor + ceiling) / 2; 73 | if (bs.get(i)) { 74 | floor = mid; 75 | } else { 76 | ceiling = mid; 77 | } 78 | } 79 | return mid; 80 | } 81 | 82 | public String encode(String lat, String lon) { 83 | 84 | return encode(Double.parseDouble(lat), Double.parseDouble(lon)); 85 | 86 | } 87 | 88 | public String encode(double lat, double lon) { 89 | BitSet latbits = getBits(lat, -90, 90); 90 | BitSet lonbits = getBits(lon, -180, 180); 91 | StringBuilder buffer = new StringBuilder(); 92 | for (int i = 0; i < numbits; i++) { 93 | buffer.append((lonbits.get(i)) ? '1' : '0'); 94 | buffer.append((latbits.get(i)) ? '1' : '0'); 95 | } 96 | return base32(Long.parseLong(buffer.toString(), 2)); 97 | } 98 | 99 | private BitSet getBits(double lat, double floor, double ceiling) { 100 | BitSet buffer = new BitSet(numbits); 101 | for (int i = 0; i < numbits; i++) { 102 | double mid = (floor + ceiling) / 2; 103 | if (lat >= mid) { 104 | buffer.set(i); 105 | floor = mid; 106 | } else { 107 | ceiling = mid; 108 | } 109 | } 110 | return buffer; 111 | } 112 | 113 | public static String base32(long i) { 114 | char[] buf = new char[65]; 115 | int charPos = 64; 116 | boolean negative = (i < 0); 117 | if (!negative) { 118 | i = -i; 119 | } 120 | while (i <= -32) { 121 | buf[charPos--] = digits[(int) (-(i % 32))]; 122 | i /= 32; 123 | } 124 | buf[charPos] = digits[(int) (-i)]; 125 | 126 | if (negative) { 127 | buf[--charPos] = '-'; 128 | } 129 | return new String(buf, charPos, (65 - charPos)); 130 | } 131 | 132 | /*********************** 获取九个的矩形编码 ****************************************/ 133 | public static String BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"; 134 | public static Map BORDERS = new HashMap(); 135 | public static Map NEIGHBORS = new HashMap(); 136 | 137 | public static void setMap() { 138 | NEIGHBORS.put("right:even", "bc01fg45238967deuvhjyznpkmstqrwx"); 139 | NEIGHBORS.put("left:even", "238967debc01fg45kmstqrwxuvhjyznp"); 140 | NEIGHBORS.put("top:even", "p0r21436x8zb9dcf5h7kjnmqesgutwvy"); 141 | NEIGHBORS.put("bottom:even", "14365h7k9dcfesgujnmqp0r2twvyx8zb"); 142 | 143 | NEIGHBORS.put("right:odd", "p0r21436x8zb9dcf5h7kjnmqesgutwvy"); 144 | NEIGHBORS.put("left:odd", "14365h7k9dcfesgujnmqp0r2twvyx8zb"); 145 | NEIGHBORS.put("top:odd", "bc01fg45238967deuvhjyznpkmstqrwx"); 146 | NEIGHBORS.put("bottom:odd", "238967debc01fg45kmstqrwxuvhjyznp"); 147 | 148 | BORDERS.put("right:even", "bcfguvyz"); 149 | BORDERS.put("left:even", "0145hjnp"); 150 | BORDERS.put("top:even", "prxz"); 151 | BORDERS.put("bottom:even", "028b"); 152 | 153 | BORDERS.put("right:odd", "prxz"); 154 | BORDERS.put("left:odd", "028b"); 155 | BORDERS.put("top:odd", "bcfguvyz"); 156 | BORDERS.put("bottom:odd", "0145hjnp"); 157 | 158 | } 159 | 160 | /** 161 | * 150 * 获取九个点的矩形编码 162 | * 151 * 163 | * 152 * @param geohash 164 | * 153 * @return 165 | * 154 166 | */ 167 | public String[] getGeoHashExpand(String geohash) { 168 | try { 169 | String geohashTop = calculateAdjacent(geohash, "top"); 170 | String geohashBottom = calculateAdjacent(geohash, "bottom"); 171 | String geohashRight = calculateAdjacent(geohash, "right"); 172 | String geohashLeft = calculateAdjacent(geohash, "left"); 173 | String geohashTopLeft = calculateAdjacent(geohashLeft, "top"); 174 | String geohashTopRight = calculateAdjacent(geohashRight, "top"); 175 | String geohashBottomRight = calculateAdjacent(geohashRight, 176 | "bottom"); 177 | String geohashBottomLeft = calculateAdjacent(geohashLeft, "bottom"); 178 | String[] expand = {geohash, geohashTop, geohashBottom, 179 | geohashRight, geohashLeft, geohashTopLeft, geohashTopRight, 180 | geohashBottomRight, geohashBottomLeft}; 181 | return expand; 182 | } catch (Exception e) { 183 | return null; 184 | } 185 | } 186 | 187 | /** 188 | * 分别计算每个点的矩形编码 189 | * 190 | * @param srcHash 191 | * @param dir 192 | * @return 193 | */ 194 | public static String calculateAdjacent(String srcHash, String dir) { 195 | srcHash = srcHash.toLowerCase(); 196 | char lastChr = srcHash.charAt(srcHash.length() - 1); 197 | int a = srcHash.length() % 2; 198 | String type = (a > 0) ? "odd" : "even"; 199 | String base = srcHash.substring(0, srcHash.length() - 1); 200 | if (BORDERS.get(dir + ":" + type).indexOf(lastChr) != -1) { 201 | base = calculateAdjacent(base, dir); 202 | } 203 | base = base 204 | + BASE32.toCharArray()[(NEIGHBORS.get(dir + ":" + type) 205 | .indexOf(lastChr))]; 206 | return base; 207 | } 208 | 209 | // 赤道半径(单位m) 210 | private static final double EARTH_RADIUS = 6371; 211 | 212 | /** 213 | * * 转化为弧度(rad) 214 | * * 215 | */ 216 | private static double rad(double d) { 217 | return d * Math.PI / 180.0; 218 | } 219 | 220 | /** 221 | * 基于googleMap中的算法得到两经纬度之间的距离,计算精度与谷歌地图的距离精度差不多,相差范围在0.2米以下 222 | * 223 | * @param lon1 第一点的精度 224 | * @param lat1 第一点的纬度 225 | * @param lon2 第二点的精度 226 | * @param lat2 第二点的纬度 227 | * @return 返回的距离,单位m 228 | */ 229 | public double getDistance(double lon1, double lat1, double lon2, 230 | double lat2) { 231 | double radLat1 = rad(lat1); 232 | double radLat2 = rad(lat2); 233 | double a = radLat1 - radLat2; 234 | double b = rad(lon1) - rad(lon2); 235 | double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) 236 | + Math.cos(radLat1) * Math.cos(radLat2) 237 | * Math.pow(Math.sin(b / 2), 2))); 238 | s = s * EARTH_RADIUS; 239 | s = Math.round(s * 1000); 240 | return s; 241 | } 242 | 243 | /** 244 | * 筛选出在范围里的经纬度集合 245 | * @param lon 经度 246 | * @param lat 纬度 247 | * @param lonLats 经纬度集合 筛选 248 | * @return 249 | */ 250 | public List> screenLonLat(Double lon, Double lat, List> lonLats) { 251 | if (null == lonLats) { 252 | return null; 253 | } 254 | List> lonLatList = new ArrayList<>(); 255 | Geohash geohash = new Geohash(); 256 | for (Map lonLat : lonLats) { 257 | Double longitude = lonLat.get("longitude"); 258 | Double latitude = lonLat.get("latitude"); 259 | Double distance = lonLat.get("distance"); 260 | //计算两个经纬度之间距离 261 | double dist = geohash.getDistance(lon, lat, longitude, latitude); 262 | if (dist <= distance) { 263 | lonLat.put("distance", (double) Math.round(dist / 1000 * 10) / 10); 264 | lonLatList.add(lonLat); 265 | } 266 | } 267 | Geohash.sortById(lonLatList); 268 | return lonLatList; 269 | } 270 | 271 | /** 272 | * 按照集合id升序排序 273 | * 274 | * @param list 275 | */ 276 | public static void sortById(List> list) { 277 | Collections.sort(list, new Comparator>() { 278 | @Override 279 | public int compare(Map u1, Map u2) { 280 | Double id1 = u1.get("distance"); 281 | Double id2 = u2.get("distance"); 282 | return id1.compareTo(id2); 283 | } 284 | }); 285 | } 286 | 287 | /** 288 | * 查询腾讯地图 关键字信息 289 | * @param keyword 290 | * @param region 291 | * @return 292 | */ 293 | public static String tencentMapList(String keyword,String region){ 294 | Geohash geohash = new Geohash(); 295 | StringBuffer stringBuffer = new StringBuffer(TENCENT_MAP_URL); 296 | stringBuffer.append("?").append("region=").append(region).append("&keyword="). 297 | append(keyword).append("&key=").append(TENCENT_MAP_CODE); 298 | return HttpRequestUtils.get(stringBuffer.toString()); 299 | } 300 | 301 | } 302 | -------------------------------------------------------------------------------- /src/main/java/me/xueyao/encryption/Base64.java: -------------------------------------------------------------------------------- 1 | package me.xueyao.encryption; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.io.UnsupportedEncodingException; 7 | 8 | /** 9 | * 描述:Base64工具类. 10 | * 11 | * @author simonxue 12 | * @version v1.0 13 | * @date:2016-09-30 14 | */ 15 | public class Base64 { 16 | 17 | private static final char[] LEGAL_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 18 | .toCharArray(); 19 | 20 | /** 21 | * The Constant base64EncodeChars. 22 | */ 23 | private static final char[] BASE64_ENCODE_CHARS = new char[]{'A', 'B', 'C', 24 | 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 25 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 26 | 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 27 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', 28 | '3', '4', '5', '6', '7', '8', '9', '+', '/'}; 29 | 30 | /** 31 | * The Constant base64DecodeChars. 32 | */ 33 | private static final byte[] BASE64_DECODE_CHARS = new byte[]{-1, -1, -1, 34 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 35 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 36 | -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 37 | 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 38 | 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 39 | 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 40 | 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, 41 | -1, -1, -1, -1}; 42 | 43 | /** 44 | * Base64编码 45 | * 46 | * @param data 47 | * @return 48 | */ 49 | public static String encode(byte[] data) { 50 | int start = 0; 51 | int len = data.length; 52 | StringBuffer buf = new StringBuffer(data.length * 3 / 2); 53 | 54 | int end = len - 3; 55 | int i = start; 56 | 57 | while (i <= end) { 58 | int d = ((((int) data[i]) & 0x0ff) << 16) 59 | | ((((int) data[i + 1]) & 0x0ff) << 8) 60 | | (((int) data[i + 2]) & 0x0ff); 61 | 62 | buf.append(LEGAL_CHARS[(d >> 18) & 63]); 63 | buf.append(LEGAL_CHARS[(d >> 12) & 63]); 64 | buf.append(LEGAL_CHARS[(d >> 6) & 63]); 65 | buf.append(LEGAL_CHARS[d & 63]); 66 | 67 | i += 3; 68 | } 69 | 70 | if (i == start + len - 2) { 71 | int d = ((((int) data[i]) & 0x0ff) << 16) 72 | | ((((int) data[i + 1]) & 255) << 8); 73 | 74 | buf.append(LEGAL_CHARS[(d >> 18) & 63]); 75 | buf.append(LEGAL_CHARS[(d >> 12) & 63]); 76 | buf.append(LEGAL_CHARS[(d >> 6) & 63]); 77 | buf.append("="); 78 | } else if (i == start + len - 1) { 79 | int d = (((int) data[i]) & 0x0ff) << 16; 80 | 81 | buf.append(LEGAL_CHARS[(d >> 18) & 63]); 82 | buf.append(LEGAL_CHARS[(d >> 12) & 63]); 83 | buf.append("=="); 84 | } 85 | 86 | return buf.toString(); 87 | } 88 | 89 | /** 90 | * Base64解码 91 | * 92 | * @param s 93 | * @return 94 | */ 95 | public static byte[] decode(String s) { 96 | 97 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 98 | try { 99 | decode(s, bos); 100 | } catch (IOException e) { 101 | throw new RuntimeException(); 102 | } 103 | byte[] decodedBytes = bos.toByteArray(); 104 | try { 105 | bos.close(); 106 | bos = null; 107 | } catch (IOException ex) { 108 | System.err.println("Error while decoding BASE64: " + ex.toString()); 109 | } 110 | return decodedBytes; 111 | } 112 | 113 | private static void decode(String s, OutputStream os) throws IOException { 114 | int i = 0; 115 | 116 | int len = s.length(); 117 | 118 | while (true) { 119 | while (i < len && s.charAt(i) <= ' ') { 120 | i++; 121 | } 122 | 123 | if (i == len) { 124 | break; 125 | } 126 | 127 | int tri = (decode(s.charAt(i)) << 18) 128 | + (decode(s.charAt(i + 1)) << 12) 129 | + (decode(s.charAt(i + 2)) << 6) 130 | + (decode(s.charAt(i + 3))); 131 | 132 | os.write((tri >> 16) & 255); 133 | if (s.charAt(i + 2) == '=') { 134 | break; 135 | } 136 | os.write((tri >> 8) & 255); 137 | if (s.charAt(i + 3) == '=') { 138 | break; 139 | } 140 | os.write(tri & 255); 141 | i += 4; 142 | } 143 | } 144 | 145 | private static int decode(char c) { 146 | if (c >= 'A' && c <= 'Z') { 147 | return ((int) c) - 65; 148 | } else if (c >= 'a' && c <= 'z') { 149 | return ((int) c) - 97 + 26; 150 | } else if (c >= '0' && c <= '9') { 151 | return ((int) c) - 48 + 26 + 26; 152 | 153 | } else { 154 | switch (c) { 155 | case '+': 156 | return 62; 157 | case '/': 158 | return 63; 159 | case '=': 160 | return 0; 161 | default: 162 | throw new RuntimeException("unexpected code: " + c); 163 | } 164 | } 165 | } 166 | 167 | /** 168 | * Encode. 169 | * 170 | * @param str the str 171 | * @param charsetName the charset name 172 | * @return the string 173 | */ 174 | public static final String encode(String str, String charsetName) { 175 | return encode(str, charsetName, 0); 176 | } 177 | 178 | /** 179 | * Encode. 180 | * 181 | * @param str the str 182 | * @param charsetName the charset name 183 | * @param width the width 184 | * @return the string 185 | */ 186 | public static final String encode(String str, String charsetName, int width) { 187 | byte[] data = null; 188 | try { 189 | data = str.getBytes(charsetName); 190 | } catch (UnsupportedEncodingException e) { 191 | e.printStackTrace(); 192 | return null; 193 | } 194 | int length = data.length; 195 | int size = (int) Math.ceil(length * 1.36); 196 | int splitsize = width > 0 ? size / width : 0; 197 | StringBuffer sb = new StringBuffer(size + splitsize); 198 | int r = length % 3; 199 | int len = length - r; 200 | int i = 0; 201 | int c; 202 | while (i < len) { 203 | c = (0x000000ff & data[i++]) << 16 | (0x000000ff & data[i++]) << 8 204 | | (0x000000ff & data[i++]); 205 | sb.append(BASE64_ENCODE_CHARS[c >> 18]); 206 | sb.append(BASE64_ENCODE_CHARS[c >> 12 & 0x3f]); 207 | sb.append(BASE64_ENCODE_CHARS[c >> 6 & 0x3f]); 208 | sb.append(BASE64_ENCODE_CHARS[c & 0x3f]); 209 | } 210 | if (r == 1) { 211 | c = 0x000000ff & data[i++]; 212 | sb.append(BASE64_ENCODE_CHARS[c >> 2]); 213 | sb.append(BASE64_ENCODE_CHARS[(c & 0x03) << 4]); 214 | sb.append("=="); 215 | } else if (r == 2) { 216 | c = (0x000000ff & data[i++]) << 8 | (0x000000ff & data[i++]); 217 | sb.append(BASE64_ENCODE_CHARS[c >> 10]); 218 | sb.append(BASE64_ENCODE_CHARS[c >> 4 & 0x3f]); 219 | sb.append(BASE64_ENCODE_CHARS[(c & 0x0f) << 2]); 220 | sb.append("="); 221 | } 222 | if (splitsize > 0) { 223 | char split = '\n'; 224 | for (i = width; i < sb.length(); i++) { 225 | sb.insert(i, split); 226 | i += width; 227 | } 228 | } 229 | return sb.toString(); 230 | } 231 | 232 | /** 233 | * Decode. 234 | * 235 | * @param str the str 236 | * @param charsetName the charset name 237 | * @return the string 238 | */ 239 | public static final String decode(String str, String charsetName) { 240 | byte[] data = null; 241 | try { 242 | data = str.getBytes(charsetName); 243 | } catch (UnsupportedEncodingException e) { 244 | e.printStackTrace(); 245 | return null; 246 | } 247 | int len = data.length; 248 | ByteArrayOutputStream buf = new ByteArrayOutputStream( 249 | (int) (len * 0.67)); 250 | int i = 0; 251 | int b1, b2, b3, b4; 252 | while (i < len) { 253 | do { 254 | if (i >= len) { 255 | b1 = -1; 256 | break; 257 | } 258 | b1 = BASE64_DECODE_CHARS[data[i++]]; 259 | } while (i < len && b1 == -1); 260 | if (b1 == -1) { 261 | break; 262 | } 263 | do { 264 | if (i >= len) { 265 | b2 = -1; 266 | break; 267 | } 268 | b2 = BASE64_DECODE_CHARS[data[i++]]; 269 | } while (i < len && b2 == -1); 270 | if (b2 == -1) { 271 | break; 272 | } 273 | buf.write((int) ((b1 << 2) | ((b2 & 0x30) >>> 4))); 274 | do { 275 | if (i >= len) { 276 | b3 = -1; 277 | break; 278 | } 279 | b3 = data[i++]; 280 | if (b3 == 61) { 281 | b3 = -1; 282 | break; 283 | } 284 | b3 = BASE64_DECODE_CHARS[b3]; 285 | } while (i < len && b3 == -1); 286 | if (b3 == -1) { 287 | break; 288 | } 289 | buf.write((int) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2))); 290 | do { 291 | if (i >= len) { 292 | b4 = -1; 293 | break; 294 | } 295 | b4 = data[i++]; 296 | if (b4 == 61) { 297 | b4 = -1; 298 | break; 299 | } 300 | b4 = BASE64_DECODE_CHARS[b4]; 301 | } while (b4 == -1); 302 | if (b4 == -1) { 303 | break; 304 | } 305 | buf.write((int) (((b3 & 0x03) << 6) | b4)); 306 | } 307 | try { 308 | return buf.toString(charsetName); 309 | } catch (UnsupportedEncodingException e) { 310 | e.printStackTrace(); 311 | return null; 312 | } 313 | } 314 | } 315 | --------------------------------------------------------------------------------