001/** 002* @Title: FaceUtilits.java 003* @Package net.gdface.utils 004* @Description: guyadong 005* @author guyadong 006* @date 2014-10-21 上åˆ10:51:32 007* @version V1.0 008*/ 009package net.gdface.utils; 010 011import java.io.BufferedWriter; 012import java.io.ByteArrayInputStream; 013import java.io.ByteArrayOutputStream; 014import java.io.File; 015import java.io.FileInputStream; 016import java.io.FileOutputStream; 017import java.io.IOException; 018import java.io.InputStream; 019import java.io.Writer; 020import java.lang.reflect.MalformedParameterizedTypeException; 021import java.lang.reflect.ParameterizedType; 022import java.lang.reflect.Type; 023import java.net.MalformedURLException; 024import java.net.URI; 025import java.net.URISyntaxException; 026import java.net.URL; 027import java.nio.ByteBuffer; 028import java.nio.channels.FileChannel; 029import java.security.MessageDigest; 030import java.security.NoSuchAlgorithmException; 031import java.util.Collection; 032import java.util.Map; 033import java.util.Map.Entry; 034import java.util.TreeMap; 035import java.util.TreeSet; 036 037/** 038 * @author guyadong 039 * 040 */ 041public class FaceUtilits { 042 private static final String MD5_REGEX = "^[a-fA-F0-9]{32}$"; 043 private static final String HEX_REGEX = "^([a-fA-F0-9]{2})+$"; 044 /** 045 * 生æˆMD5æ ¡éªŒç 046 * 047 * @param source 048 * @return 049 */ 050 static public byte[] getMD5(byte[] source) { 051 if (Judge.isNull(source)){ 052 return null; 053 } 054 try { 055 MessageDigest md = MessageDigest.getInstance("MD5"); 056 return md.digest(source); 057 } catch (NoSuchAlgorithmException e) { 058 throw new RuntimeException(e); 059 } 060 } 061 /** 062 * 返回buffer䏿‰€æœ‰å—节(position~limit),䏿”¹å˜bufferçŠ¶æ€ 063 * @param buffer 064 * @return buffer 为 null 时返回 null 065 */ 066 public static final byte[] getBytesInBuffer(ByteBuffer buffer){ 067 if(null == buffer){ 068 return null; 069 } 070 int pos = buffer.position(); 071 try{ 072 byte[] bytes = new byte[buffer.remaining()]; 073 buffer.get(bytes); 074 return bytes; 075 }finally{ 076 buffer.position(pos); 077 } 078 } 079 /** 080 * 生æˆMD5æ ¡éªŒç 081 * @param source 082 * @return 083 * @see #getMD5(byte[]) 084 */ 085 static public ByteBuffer getMD5(ByteBuffer source) { 086 return null == source ?null:ByteBuffer.wrap(getMD5(getBytesInBuffer(source))); 087 } 088 /** 089 * å°†16ä½byte[] 转æ¢ä¸º32ä½çš„HEXæ ¼å¼çš„å—符串String 090 * 091 * @param buffer 092 * @return 093 */ 094 static public String toHex(byte[] buffer) { 095 if (Judge.isNull(buffer)){ 096 return null; 097 } 098 StringBuffer sb = new StringBuffer(buffer.length * 2); 099 for (int i = 0; i < buffer.length; i++) { 100 sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16)); 101 sb.append(Character.forDigit(buffer[i] & 15, 16)); 102 } 103 return sb.toString(); 104 } 105 /** @see #toHex(byte[]) */ 106 static public String toHex(ByteBuffer buffer) { 107 return toHex(getBytesInBuffer(buffer)); 108 } 109 /** 110 * å—符串验è¯å™¨,æ ¹æ®æ£åˆ™è¡¨è¾¾å¼åˆ¤æ–å—符串是å¦ä¸ºåå…进制(HEX)å—符串 111 * 输入为null或空或æ£åˆ™è¡¨è¾¾å¼ä¸åŒ¹é…则返回false 112 */ 113 public boolean validHEX(String input){ 114 return input != null && input.matches(HEX_REGEX); 115 } 116 public static byte[] hex2Bytes(String src){ 117 if(null == src){ 118 return null; 119 } 120 byte[] res = new byte[src.length()/2]; 121 char[] chs = src.toCharArray(); 122 int[] b = new int[2]; 123 124 for(int i=0,c=0; i<chs.length; i+=2,c++){ 125 for(int j=0; j<2; j++){ 126 if(chs[i+j]>='0' && chs[i+j]<='9'){ 127 b[j] = (chs[i+j]-'0'); 128 }else if(chs[i+j]>='A' && chs[i+j]<='F'){ 129 b[j] = (chs[i+j]-'A'+10); 130 }else if(chs[i+j]>='a' && chs[i+j]<='f'){ 131 b[j] = (chs[i+j]-'a'+10); 132 } 133 } 134 135 b[0] = (b[0]&0x0f)<<4; 136 b[1] = (b[1]&0x0f); 137 res[c] = (byte) (b[0] | b[1]); 138 } 139 140 return res; 141 } 142 public static ByteBuffer hex2ByteBuffer(String src){ 143 return null == src?null:ByteBuffer.wrap(hex2Bytes(src)); 144 } 145 /** 146 * 生æˆMD5æ ¡éªŒç å—符串 147 * 148 * @param source 149 * @return 150 * @see #getMD5(byte[]) 151 * @see #toHex(byte[]) 152 */ 153 static public String getMD5String(byte[] source) { 154 return toHex(getMD5(source)); 155 } 156 /** 157 * 生æˆMD5æ ¡éªŒç å—符串 158 * 159 * @param source 160 * @return 161 * @see #getMD5(byte[]) 162 * @see #toHex(byte[]) 163 */ 164 static public String getMD5String(ByteBuffer source) { 165 return toHex(getMD5(source)); 166 } 167 /** 168 * åˆ¤æ–æ˜¯å¦ä¸ºæœ‰æ•ˆçš„MD5å—符串 169 * @return 170 */ 171 public static final boolean validMd5(String md5){ 172 return null != md5 && md5.matches(MD5_REGEX); 173 } 174 /** 175 * 从{@link InputStream}读å–å—节数组<br> 176 * 当{@code in}为{@link FileInputStream}时,调用{@link #readBytes(FileInputStream)}(NIOæ–¹å¼)读å–<br> 177 * ç»“æŸæ—¶ä¼šå…³é—{@link InputStream} 178 * @param in 179 * @return 180 * @throws IOException 181 * @throws IllegalArgumentException {@code in}为{@code null} 182 */ 183 public static byte[] readBytes(InputStream in) throws IOException, IllegalArgumentException { 184 Assert.notNull(in, "in"); 185 if(in instanceof FileInputStream){ 186 return readBytes((FileInputStream)in); 187 } 188 try { 189 int buffSize = Math.max(in.available(), 1024*8); 190 byte[] temp = new byte[buffSize]; 191 ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize); 192 int size = 0; 193 while ((size = in.read(temp)) != -1) { 194 out.write(temp, 0, size); 195 } 196 return out.toByteArray(); 197 } finally { 198 in.close(); 199 } 200 } 201 202 /** 203 * NIOæ–¹å¼ä»Ž{@link FileInputStream}读å–å—节数组<br> 204 * ç»“æŸæ—¶ä¼šå…³é—{@link InputStream} 205 * @param fin {@link FileInputStream} 206 * @return 返回读å–çš„å—节数 当{@code fin}为null时返回null; 207 * @throws IOException 208 */ 209 public static byte[] readBytes(FileInputStream fin) throws IOException { 210 Assert.notNull(fin, "fin"); 211 FileChannel fc = fin.getChannel(); 212 try { 213 ByteBuffer bb = ByteBuffer.allocate((int) fc.size()); 214 fc.read(bb); 215 bb.flip(); 216 return bb.array(); 217 } finally { 218 if (null != fc){ 219 fc.close(); 220 } 221 fin.close(); 222 } 223 } 224 /** 225 * 将对象转æ¢ä¸ºInputStream<br> 226 * 类型å¯ä»¥æ˜¯byte[],{@link ByteBuffer},{@link InputStream},{@link String}(base64ç¼–ç ),{@link File},{@link URL},{@link URI},å¦åˆ™æŠ›å‡ºRuntimeException<br> 227 * 228 * @param src 229 * 获å–InputStreamçš„æºå¯¹è±¡ 230 * @return 返回获å–çš„InputStream对象,src为null失败返回null或抛出异常 231 * @throws IOException 232 * @throws IllegalArgumentException æ— æ³•ä»Ž{@code src}获å–{@link InputStream} 233 */ 234 public static <T> InputStream getInputStream(T src) throws IOException, IllegalArgumentException { 235 Assert.notNull(src, "src"); 236 if (src instanceof InputStream){ 237 return (InputStream) src; 238 }else if (src instanceof String) { 239 return new ByteArrayInputStream(Base64Utils.decode(((String) src))); 240 } else if (src instanceof byte[]) { 241 return new ByteArrayInputStream((byte[]) src); 242 } else if (src instanceof ByteBuffer) { 243 return new ByteArrayInputStream(getBytesInBuffer((ByteBuffer) src)); 244 } else if (src instanceof File) { 245 return new FileInputStream((File) src); 246 } else if (src instanceof URL) { 247 return ((URL) src).openStream(); 248 } else if (src instanceof URI) { 249 return ((URI) src).toURL().openStream(); 250 } else{ 251 throw new IllegalArgumentException(String.format("Can't get inputstream from [%s]", src.getClass() 252 .getCanonicalName())); 253 } 254 } 255 256 /** 257 * 将数æ®å¯¹è±¡{@code src}转æ¢ä¸ºå—节数组(byte[])<br> 258 * {@code src}的数æ®ç±»åž‹å¯ä»¥æ˜¯byte[],{@link InputStream},{@link ByteBuffer},{@link String}(base64ç¼–ç ),{@link File},{@link URL},{@link URI} 259 * å¦åˆ™æŠ›å‡º{@link IllegalArgumentException}<br> 260 * 对象转æ¢ä¸ºInputStream或byte[]æ—¶,å¯èƒ½ä¼šæŠ›å‡º{@link IOException} 261 * 262 * 当{@code src}为{@link File}或{@link FileInputStream}时,使用NIOæ–¹å¼({@link #readBytes(FileInputStream)})è¯»å– 263 * 264 * @param src 265 * 获å–byte[]çš„æºå¯¹è±¡ 266 * @return 返回å—节数组,傿•°ä¸º{@code null}或类型ä¸å¯¹åˆ™æŠ›å‡ºå¼‚常 267 * @throws IOException 268 * @throws IllegalArgumentException {@code src}为{@code null}æˆ–æ— æ³•ä»Ž{@code src}获å–{@link InputStream} 269 * @see #readBytes(InputStream) 270 * @see #readBytes(FileInputStream) 271 * @see #getInputStream(Object) 272 * @see Base64Utils#decode(String) 273 */ 274 static public final <T> byte[] getBytes(T src) throws IOException, IllegalArgumentException { 275 Assert.notNull(src, "src"); 276 if (src instanceof byte[]) { 277 return (byte[]) src; 278 } else if (src instanceof String) { 279 return Base64Utils.decode(((String) src)); 280 } else if (src instanceof ByteBuffer) { 281 return getBytesInBuffer((ByteBuffer)src); 282 } else if (src instanceof FileInputStream){ 283 return readBytes((FileInputStream)src); 284 }else if (src instanceof File){ 285 return readBytes(new FileInputStream((File)src)); 286 }else { 287 return readBytes(getInputStream(src)); 288 } 289 } 290 291 /** 292 * 调用 {@link #getBytes(Object)}返回éžç©ºå—节数组<br> 293 * 如果返回{@code null}或空å—节数组,则抛出{@link IOException}<br> 294 * @param src 295 * @return 296 * @throws IOException 297 * @throws IllegalArgumentException 298 * @see #getBytes(Object) 299 */ 300 static public final <T> byte[] getBytesNotEmpty(T src) throws IOException, IllegalArgumentException { 301 byte[] imgData = getBytes(src); 302 if (Judge.isEmpty(imgData)){ 303 throw new IOException(String.format("return null or zero length from %s", src.getClass() 304 .getSimpleName())); 305 } 306 return imgData; 307 } 308 309 /** 310 * 将数æ®å¯¹è±¡src转æ¢ä¸º{@link ByteBuffer} 311 * @param src 312 * @return 313 * @throws IOException 314 * @throws IllegalArgumentException 315 * @see #getBytes(Object) 316 */ 317 static public final <T> ByteBuffer getByteBuffer(T src) throws IOException, IllegalArgumentException { 318 return ByteBuffer.wrap(getBytes(src)); 319 } 320 /** 321 * 调用 {@link #getByteBuffer(Object)}返回éžç©º{@link ByteBuffer}<br> 322 * 如果返回{@code null}或空,则抛出{@link IOException}<br> 323 * @param src 324 * @return 325 * @throws IOException 326 * @throws IllegalArgumentException 327 */ 328 static public final <T> ByteBuffer getByteBufferNotEmpty(T src) throws IOException, IllegalArgumentException { 329 return ByteBuffer.wrap(getBytesNotEmpty(src)); 330 } 331 /** 332 * 将图片数æ®ä¿å˜åœ¨folder指定的文件夹下,文件å用图片的md5æ ¡éªŒç 命å,è‡ªåŠ¨åˆ¤æ–æ–‡ä»¶åŽç¼€<br> 333 * 334 * @param img 335 * å›¾åƒæ•°æ® 336 * @param folder 337 * 文件ä¿å˜çš„ä½ç½® 338 * @return 返回ä¿å˜çš„æ–‡ä»¶å,如果{@code img}䏿— æ³•èŽ·å–æ ¼å¼åï¼Œåˆ™è§†ä¸ºæ— æ•ˆæ•°æ®ï¼Œä¸ä¿å˜ï¼Œè¿”回null; 339 * @throws IOException 340 * 调用{@link FaceUtilitsX#getFormatName(byte[])}获å–å›¾åƒæ ¼å¼å称出错或其他IO异常 341 * @throws IllegalArgumentException 342 * {@code data}为null或空时 343 * @see #saveBytes(byte[], File, boolean) 344 */ 345 public static File saveImageAutoName(byte[] img, File folder) throws IOException, IllegalArgumentException { 346 Assert.notEmpty(img, "img"); 347 File file = new File(folder, getMD5String(img) + "."+FaceUtilitsX.getFormatName(img).toLowerCase()); 348 return saveBytes(img,file,file.exists()&&file.isFile()&&0==file.length()); 349 } 350 351 /** 352 * @param img 353 * @param folder 354 * 文件ä¿å˜ä½ç½® 355 * @throws IOException 356 * æ•°æ®éžå¯è¯†åˆ«çš„å›¾åƒæ ¼å¼æˆ–å…¶ä»–IO异常 357 * @throws IllegalArgumentException 358 * {@code img}为null 359 * @see #saveImageAutoName(byte[], File) 360 * @return 返回ä¿å˜çš„æ–‡ä»¶,如果从{@code img}ä¸è¯»å–的数æ®ä¸ºç©ºè¿”回null 361 */ 362 public static File saveImage(InputStream img, File folder) throws IOException, IllegalArgumentException { 363 Assert.notNull(img, "img"); 364 byte[] imgData = readBytes(img); 365 return Judge.isEmpty(imgData)?null:saveImageAutoName(imgData,folder); 366 } 367 368 /** 369 * å°†{@code URL}å—符串转æ¢ä¸º{@code URI}对象<br> 370 * 在转æ¢è¿‡ç¨‹ä¸ä¼šå°†è‡ªåЍ坹ä¸ç¬¦åˆURI规范的å—符进行编ç ,<br> 371 * 在转æ¢è¿‡ç¨‹ä¸å…ˆä»Žå—符串生æˆ{@code URL}对象,如果{@code String}ä¸èƒ½è½¬æ¢æˆURL对象,则抛出异常 372 * @param urlStr 373 * @return 374 * @throws MalformedURLException 375 */ 376 public static URI createURI(String urlStr) throws MalformedURLException{ 377 try { 378 return new URI(urlStr); 379 } catch (URISyntaxException e) { 380 try { 381 URL url=new URL(urlStr); 382 return new URI(url.getProtocol(),url.getUserInfo(),url.getHost(),url.getPort(),url.getPath(),url.getQuery(),url.getRef()); 383 } catch (URISyntaxException e1) { 384 throw new RuntimeException(e1); 385 } 386 } 387 } 388 389 /** 390 * NIOæ–¹å¼å°†{@code data}æ•°æ®ä¿å˜åœ¨{@code file}指定的文件ä¸<br> 391 * 如果{@code file}所在文件夹ä¸å˜åœ¨ï¼Œåˆ™ä¼šè‡ªåŠ¨åˆ›å»ºæ‰€æœ‰çš„æ–‡ä»¶å¤¹<br> 392 * @param data 393 * @param file 文件ä¿å˜çš„ä½ç½® 394 * @param overwrite åŒå文件å˜åœ¨æ—¶æ˜¯å¦è¦†ç›– 395 * @return 返回ä¿å˜çš„æ–‡ä»¶å 396 * @throws IOException {@code file}å˜åœ¨ä½†ä¸æ˜¯æ–‡ä»¶æˆ–å…¶ä»–IO异常 397 * @throws IllegalArgumentException {@code data}为nullæ—¶ 398 */ 399 public static File saveBytes(byte[] data, File file, boolean overwrite) throws IOException, 400 IllegalArgumentException { 401 Assert.notNull(data, "data"); 402 FileOutputStream out = null; 403 FileChannel fc = null; 404 try { 405 File folder = file.getParentFile(); 406 if (!folder.exists()){ 407 folder.mkdirs(); 408 } 409 long free = folder.getFreeSpace()>>20;//å¯ç”¨ç£ç›˜ç©ºé—´(MB) 410 if(free<10){ 411 throw new IOException(String.format("DISK ALMOST FULL(ç£ç›˜ç©ºé—´ä¸è¶³) FREE %dMB,%s",free,folder.getAbsolutePath())); 412 } 413 if (!file.exists() || !file.isFile() || overwrite) { 414 out = new FileOutputStream(file); 415 fc = out.getChannel(); 416 ByteBuffer bb = ByteBuffer.wrap(data); 417 fc.write(bb); 418 } 419 return file; 420 } finally { 421 if (null != fc){ 422 fc.close(); 423 } 424 if (null != out){ 425 out.close(); 426 } 427 } 428 } 429 430 /** 431 * å¦‚æžœæ— æ³•èŽ·å–æ³›åž‹å‚数对象,返回null 432 * @param clazz 433 * @return 434 * @see #getParameterizedType(Class) 435 */ 436 public static Class<?>[] getParameterizedTypeNoThrow(Class<?>clazz) { 437 try{ 438 Class<?>[] types=getParameterizedType(clazz); 439 return types; 440 }catch(Exception e){ 441 return null; 442 } 443 } 444 /** 445 * 返回{@code clazz}æ³›åž‹è¶…ç±»çš„å‚æ•°å¯¹è±¡<br> 446 * å¦‚æžœè¶…ç±»ä¸æ˜¯æ³›åž‹å¯¹è±¡ï¼Œåˆ™æŠ›å‡º{@link IllegalArgumentException}<br> 447 * @param clazz 448 * @return 449 * @throws MalformedParameterizedTypeException è¶…ç±»ä¸æ˜¯æ³›åž‹ç±» 450 * @throws IllegalArgumentException æ— æ³•èŽ·å–å®žé™…æ³›åž‹å‚æ•°å¯¹è±¡ç±»åž‹ 451 */ 452 public static Class<?>[] getParameterizedType(Class<?>clazz) throws MalformedParameterizedTypeException, IllegalArgumentException{ 453 Type partype = clazz.getGenericSuperclass(); 454 if(!(partype instanceof ParameterizedType)){ 455 //è¶…ç±»ä¸æ˜¯æ³›åž‹ 456 throw new IllegalArgumentException(String.format("superclass of %s not ParameterizedType(è¶…ç±»ä¸æ˜¯æ³›åž‹ç±»)",clazz.getName())); 457 } 458 Type[] types = ((ParameterizedType) partype).getActualTypeArguments(); 459 if(!(types[0] instanceof Class<?>)){ 460 System.err.print("cant'not get class for ParameterizedType (æ— æ³•èŽ·å–å®žé™…æ³›åž‹å‚æ•°å¯¹è±¡ç±»åž‹(Class))"); 461 throw new MalformedParameterizedTypeException(); 462 } 463 Class<?>[] paramClass=new Class<?>[types.length]; 464 for(int i=0;i<paramClass.length;i++){ 465 paramClass[i]=(Class<?>) types[i]; 466 } 467 return paramClass; 468 } 469 470 public final static Throwable getCause(Throwable e) { 471 return e==null?null:(e.getCause()==null?e:e.getCause()); 472 } 473 474 /** 475 * 以递归方å¼è¿”回被{@code shellClass}多层å°è£…的异常<br> 476 * @param e 477 * @param shellClass å°è£…异常的类 478 * @return 479 */ 480 public static final Throwable stripThrowableShell(Throwable e, Class<? extends Throwable> shellClass){ 481 if(!Judge.hasNull(e,e.getCause())&&e.getClass()==shellClass){ 482 return stripThrowableShell(e.getCause(), shellClass); 483 } 484 return e; 485 } 486 487 /** 488 * 对{@link Map}ä¸å…ƒç´ 以key排åºåŽï¼Œæ¯è¡Œä»¥{key}={value}å½¢å¼è¾“出到{@link Writer}<br> 489 * map为空或null时则ä¸å‘writer写入任何内容 490 * @param map 491 * @param writer 为null抛出{@link IllegalArgumentException} 492 * @param lineSeparator æ¢è¡Œç¬¦,为null则使用系统默认的æ¢è¡Œç¬¦(windows \n linux \r\n) 493 * @throws IOException 494 */ 495 public static void storeSortedMap(Map<String,String> map,Writer writer, String lineSeparator) throws IOException { 496 Assert.notNull(writer, "writer"); 497 TreeMap<String, String> sortedMap = new TreeMap<String,String>(); 498 if(null!=map){ 499 sortedMap.putAll(map); 500 } 501 BufferedWriter bw=(writer instanceof BufferedWriter)?(BufferedWriter)writer 502 : new BufferedWriter(writer); 503 for (Entry<String,String> e:sortedMap.entrySet()) { 504 bw.write(e.getKey() + "=" + e.getValue()); 505 if(null==lineSeparator){ 506 bw.newLine(); 507 }else{ 508 bw.write("\n"); 509 } 510 } 511 bw.flush(); 512 } 513 514 /** 515 * 对 {@link Collection}ä¸å…ƒç´ 排åºåŽ(去除é‡å¤)ï¼Œå…ƒç´ åˆ†è¡Œè¾“å‡ºåˆ°{@link Writer}<br> 516 * collection为空或null时则ä¸å‘writer写入任何内容 517 * @param collection 518 * @param writer 为null抛出{@link IllegalArgumentException} 519 * @param lineSeparator æ¢è¡Œç¬¦,为null则使用系统默认的æ¢è¡Œç¬¦(windows \n linux \r\n) 520 * @throws IOException 521 */ 522 public static void storeSortedSet(Collection<String> collection,Writer writer, String lineSeparator) throws IOException { 523 Assert.notNull(writer, "writer"); 524 TreeSet<String> sortedSet = new TreeSet<String>(); 525 if(null!=collection){ 526 sortedSet.addAll(collection); 527 } 528 BufferedWriter bw=(writer instanceof BufferedWriter)?(BufferedWriter)writer 529 : new BufferedWriter(writer); 530 for (String e:sortedSet) { 531 bw.write(e); 532 if(null==lineSeparator){ 533 bw.newLine(); 534 }else{ 535 bw.write("\n"); 536 } 537 } 538 bw.flush(); 539 } 540 541 /** 542 * 比较两个Map是å¦ç›¸ç‰ 543 * @param m1 544 * @param m2 545 * @return 546 */ 547 public static <K,V>boolean equals(Map<K,V> m1,Map<K,V> m2){ 548 if(m1==m2){ 549 return true; 550 } 551 if(null ==m1 || null ==m2){ 552 return false; 553 } 554 if(m1.size() != m2.size()){ 555 return false; 556 } 557 for(Entry<K, V> entry:m1.entrySet()){ 558 K key = entry.getKey(); 559 if(!m2.containsKey(key)){ 560 return false; 561 } 562 V v1 = entry.getValue(); 563 V v2 = m2.get(key); 564 if(v1 ==v2 ) { 565 continue; 566 } 567 if(null ==v1 || null ==v2){ 568 return false; 569 } 570 if(!v1.equals(v2)){ 571 return false; 572 } 573 } 574 return true; 575 } 576}