001/** 002* @Title: StreamUtilits.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 gu.sql2java.store; 010 011import java.io.ByteArrayInputStream; 012import java.io.ByteArrayOutputStream; 013import java.io.File; 014import java.io.FileInputStream; 015import java.io.FileOutputStream; 016import java.io.IOException; 017import java.io.InputStream; 018import java.net.URI; 019import java.net.URL; 020import java.nio.ByteBuffer; 021import java.nio.channels.FileChannel; 022import java.security.MessageDigest; 023import java.security.NoSuchAlgorithmException; 024 025/** 026 * 二进制数据(byte[],ByteBuffer)工具类 027 * @author guyadong 028 * 029 */ 030public class BinaryUtils { 031 private static final String MD5_REGEX = "^[a-fA-F0-9]{32}$"; 032 private static final String HEX_REGEX = "^([a-fA-F0-9]{2})+$"; 033 /** 034 * 生成MD5校验码 035 * 036 * @param source 037 * @return 038 */ 039 public static final byte[] getMD5(byte[] source) { 040 if (null ==source){ 041 return null; 042 } 043 try { 044 MessageDigest md = MessageDigest.getInstance("MD5"); 045 return md.digest(source); 046 } catch (NoSuchAlgorithmException e) { 047 throw new RuntimeException(e); 048 } 049 } 050 /** 051 * 返回buffer中所有字节(position~limit),不改变buffer状态 052 * @param buffer 053 * @return buffer 为 null 时返回 null 054 */ 055 public static final byte[] getBytesInBuffer(ByteBuffer buffer){ 056 if(null == buffer){ 057 return null; 058 } 059 int pos = buffer.position(); 060 try{ 061 byte[] bytes = new byte[buffer.remaining()]; 062 buffer.get(bytes); 063 return bytes; 064 }finally{ 065 buffer.position(pos); 066 } 067 } 068 /** 069 * 生成MD5校验码 070 * @param source 071 * @return 072 * @see #getMD5(byte[]) 073 */ 074 public static final ByteBuffer getMD5(ByteBuffer source) { 075 return null == source ?null:ByteBuffer.wrap(getMD5(getBytesInBuffer(source))); 076 } 077 /** 078 * 将16位byte[] 转换为32位的HEX格式的字符串String 079 * 080 * @param buffer 081 * @return 082 */ 083 public static final String toHex(byte[] buffer) { 084 if (null == buffer){ 085 return null; 086 } 087 StringBuffer sb = new StringBuffer(buffer.length * 2); 088 for (int i = 0; i < buffer.length; i++) { 089 sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16)); 090 sb.append(Character.forDigit(buffer[i] & 15, 16)); 091 } 092 return sb.toString(); 093 } 094 /** @see #toHex(byte[]) */ 095 public static final String toHex(ByteBuffer buffer) { 096 return toHex(getBytesInBuffer(buffer)); 097 } 098 /** 099 * 字符串验证器,根据正则表达式判断字符串是否为十六进制(HEX)字符串 100 * 输入为null或空或正则表达式不匹配则返回false 101 */ 102 public static final boolean validHEX(String input){ 103 return input != null && input.matches(HEX_REGEX); 104 } 105 /** 106 * convert HEX string to byte array 107 * @param src 108 * @return byte array or {@code null} if src is {@code null} 109 */ 110 public static final byte[] hex2Bytes(String src){ 111 if(null == src){ 112 return null; 113 } 114 byte[] res = new byte[src.length()/2]; 115 char[] chs = src.toCharArray(); 116 int[] b = new int[2]; 117 118 for(int i=0,c=0; i<chs.length; i+=2,c++){ 119 for(int j=0; j<2; j++){ 120 if(chs[i+j]>='0' && chs[i+j]<='9'){ 121 b[j] = (chs[i+j]-'0'); 122 }else if(chs[i+j]>='A' && chs[i+j]<='F'){ 123 b[j] = (chs[i+j]-'A'+10); 124 }else if(chs[i+j]>='a' && chs[i+j]<='f'){ 125 b[j] = (chs[i+j]-'a'+10); 126 } 127 } 128 129 b[0] = (b[0]&0x0f)<<4; 130 b[1] = (b[1]&0x0f); 131 res[c] = (byte) (b[0] | b[1]); 132 } 133 134 return res; 135 } 136 /** 137 * convert HEX string to ByteBuffer 138 * @param src 139 * @return byte array or {@code null} if src is {@code null} 140 */ 141 public static final ByteBuffer hex2ByteBuffer(String src){ 142 return null == src?null:ByteBuffer.wrap(hex2Bytes(src)); 143 } 144 /** 145 * 生成MD5校验码字符串 146 * 147 * @param source 148 * @return 149 * @see #getMD5(byte[]) 150 * @see #toHex(byte[]) 151 */ 152 public static final String getMD5String(byte[] source) { 153 return toHex(getMD5(source)); 154 } 155 /** 156 * 生成MD5校验码字符串 157 * 158 * @param source 159 * @return 160 * @see #getMD5(byte[]) 161 * @see #toHex(byte[]) 162 */ 163 public static final String getMD5String(ByteBuffer source) { 164 return toHex(getMD5(source)); 165 } 166 /** 167 * 判断是否为有效的MD5字符串 168 * @return 169 */ 170 public static final boolean validMd5(String md5){ 171 return null != md5 && md5.matches(MD5_REGEX); 172 } 173 /** 174 * 从{@link InputStream}读取字节数组<br> 175 * 当{@code in}为{@link FileInputStream}时,调用{@link #readBytes(FileInputStream)}(NIO方式)读取<br> 176 * 结束时会关闭{@link InputStream} 177 * @param in 为{@code null}返回{@code null} 178 * @return 179 * @throws IOException 180 * @throws IllegalArgumentException {@code in}为{@code null} 181 */ 182 public static final byte[] readBytes(InputStream in) throws IOException, IllegalArgumentException { 183 if(null == in){ 184 return null; 185 } 186 if(in instanceof FileInputStream){ 187 return readBytes((FileInputStream)in); 188 } 189 try { 190 int buffSize = Math.max(in.available(), 1024*8); 191 byte[] temp = new byte[buffSize]; 192 ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize); 193 int size = 0; 194 while ((size = in.read(temp)) != -1) { 195 out.write(temp, 0, size); 196 } 197 return out.toByteArray(); 198 } finally { 199 in.close(); 200 } 201 } 202 203 /** 204 * NIO方式从{@link FileInputStream}读取字节数组<br> 205 * 结束时会关闭{@link InputStream} 206 * @param fin {@link FileInputStream} 207 * @return 返回读取的字节数 当{@code fin}为null时返回null; 208 * @throws IOException 209 */ 210 public static final byte[] readBytes(FileInputStream fin) throws IOException { 211 if(null == fin){ 212 return null; 213 } 214 FileChannel fc = fin.getChannel(); 215 try { 216 ByteBuffer bb = ByteBuffer.allocate((int) fc.size()); 217 fc.read(bb); 218 bb.flip(); 219 return bb.array(); 220 } finally { 221 if (null != fc){ 222 fc.close(); 223 } 224 fin.close(); 225 } 226 } 227 /** 228 * 将对象转换为InputStream<br> 229 * 类型可以是byte[],{@link ByteBuffer},{@link InputStream},{@link String}(base64编码),{@link File},{@link URL},{@link URI},否则抛出RuntimeException<br> 230 * 231 * @param src 232 * 获取InputStream的源对象 233 * @return 返回获取的InputStream对象,src为{@code null}返回{@code null},类型错误抛出异常 234 * @throws IOException 235 * @throws IllegalArgumentException 无法从{@code src}获取{@link InputStream} 236 */ 237 public static final <T> InputStream getInputStream(T src) throws IOException, IllegalArgumentException { 238 if(null == src){ 239 return null; 240 } 241 if (src instanceof InputStream){ 242 return (InputStream) src; 243 }else if (src instanceof String) { 244 return new ByteArrayInputStream(Base64Utils.decode(((String) src))); 245 } else if (src instanceof byte[]) { 246 return new ByteArrayInputStream((byte[]) src); 247 } else if (src instanceof ByteBuffer) { 248 return new ByteArrayInputStream(getBytesInBuffer((ByteBuffer) src)); 249 } else if (src instanceof File) { 250 return new FileInputStream((File) src); 251 } else if (src instanceof URL) { 252 return ((URL) src).openStream(); 253 } else if (src instanceof URI) { 254 return ((URI) src).toURL().openStream(); 255 } else{ 256 throw new IllegalArgumentException(String.format("Can't get inputstream from [%s]", src.getClass() 257 .getCanonicalName())); 258 } 259 } 260 261 /** 262 * 将数据对象{@code src}转换为字节数组(byte[])<br> 263 * {@code src}的数据类型可以是byte[],{@link InputStream},{@link ByteBuffer},{@link String}(base64编码),{@link File},{@link URL},{@link URI} 264 * 否则抛出{@link IllegalArgumentException}<br> 265 * 对象转换为InputStream或byte[]时,可能会抛出{@link IOException} 266 * 267 * 当{@code src}为{@link File}或{@link FileInputStream}时,使用NIO方式({@link #readBytes(FileInputStream)})读取 268 * 269 * @param src 270 * 获取byte[]的源对象 271 * @return 返回字节数组,参数为{@code null}返回{@code null},类型不对则抛出异常 272 * @throws IOException 273 * @throws IllegalArgumentException {@code src}为{@code null}或无法从{@code src}获取{@link InputStream} 274 * @see #readBytes(InputStream) 275 * @see #readBytes(FileInputStream) 276 * @see #getInputStream(Object) 277 * @see Base64Utils#decode(String) 278 */ 279 public static final <T> byte[] getBytes(T src) throws IOException, IllegalArgumentException { 280 if(null==src){ 281 return null; 282 } 283 if (src instanceof byte[]) { 284 return (byte[]) src; 285 } else if (src instanceof String) { 286 return Base64Utils.decode(((String) src)); 287 } else if (src instanceof ByteBuffer) { 288 return getBytesInBuffer((ByteBuffer)src); 289 } else if (src instanceof FileInputStream){ 290 return readBytes((FileInputStream)src); 291 }else if (src instanceof File){ 292 return readBytes(new FileInputStream((File)src)); 293 }else { 294 return readBytes(getInputStream(src)); 295 } 296 } 297 298 /** 299 * 调用 {@link #getBytes(Object)}返回非空字节数组<br> 300 * 如果返回{@code null}或空字节数组,则抛出{@link IOException}<br> 301 * @param src 获取byte[]的源对象 302 * @return 返回非空字节数组 303 * @throws IOException 304 * @see #getBytes(Object) 305 */ 306 public static final <T> byte[] getBytesNotEmpty(T src) throws IOException { 307 byte[] imgData = getBytes(src); 308 if (imgData == null || imgData.length == 0){ 309 throw new IOException(String.format("return null or zero length from %s", src.getClass() 310 .getSimpleName())); 311 } 312 return imgData; 313 } 314 315 /** 316 * 将数据对象src转换为{@link ByteBuffer} 317 * @param src 获取byte[]的源对象 318 * @return 返回字节数组,参数为{@code null}返回{@code null},类型不对则抛出异常 319 * @throws IOException 320 * @throws IllegalArgumentException 无法从{@code src}获取{@link InputStream} 321 * @see #getBytes(Object) 322 */ 323 public static final <T> ByteBuffer getByteBuffer(T src) throws IOException, IllegalArgumentException { 324 return src == null ? null : ByteBuffer.wrap(getBytes(src)); 325 } 326 327 /** 328 * 调用 {@link #getByteBuffer(Object)}返回非空{@link ByteBuffer}<br> 329 * 如果返回{@code null}或空,则抛出{@link IOException}<br> 330 * @param src 331 * @return 返回非空字节数组 332 * @throws IOException 333 * @throws IllegalArgumentException 334 */ 335 public static final <T> ByteBuffer getByteBufferNotEmpty(T src) throws IOException, IllegalArgumentException { 336 return ByteBuffer.wrap(getBytesNotEmpty(src)); 337 } 338 /** 339 * NIO方式将{@code data}数据保存在{@code file}指定的文件中<br> 340 * 如果{@code file}所在文件夹不存在,则会自动创建所有的文件夹<br> 341 * @param data 342 * @param file 文件保存的位置 343 * @param overwrite 同名文件存在时是否覆盖 344 * @return 返回保存的文件名 345 * @throws IOException {@code file}存在但不是文件或其他IO异常 346 * @throws IllegalArgumentException {@code data}为null时 347 */ 348 public static final File saveBytes(byte[] data, File file, boolean overwrite) throws IOException, 349 IllegalArgumentException { 350 if(data == null){ 351 throw new IllegalArgumentException("data is null"); 352 } 353 FileOutputStream out = null; 354 FileChannel fc = null; 355 try { 356 File folder = file.getParentFile(); 357 if (!folder.exists()){ 358 folder.mkdirs(); 359 } 360 long free = folder.getFreeSpace()>>20;//可用磁盘空间(MB) 361 if(free<10){ 362 throw new IOException(String.format("DISK ALMOST FULL(磁盘空间不足) FREE %dMB,%s",free,folder.getAbsolutePath())); 363 } 364 if (!file.exists() || !file.isFile() || overwrite) { 365 out = new FileOutputStream(file); 366 fc = out.getChannel(); 367 ByteBuffer bb = ByteBuffer.wrap(data); 368 fc.write(bb); 369 } 370 return file; 371 } finally { 372 if (null != fc){ 373 fc.close(); 374 } 375 if (null != out){ 376 out.close(); 377 } 378 } 379 } 380}