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}