package org.theonefx.wcframework.utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.Flushable; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PushbackInputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.io.Writer; import org.theonefx.wcframework.core.resource.ClassScanner; import org.theonefx.wcframework.core.resource.Resource; /** * 提供了一组创建 Reader/Writer/InputStream/OutputStream 的便利函数 * * @author zozoh(zozohtnt@gmail.com) * @author Wendal(wendal1985@gmail.com) * @author bonyfish(mc02cxj@gmail.com) */ public abstract class StreamUtils { private static final int BUF_SIZE = 8192; /** * 判断两个输入流是否严格相等 */ public static boolean equals(InputStream sA, InputStream sB) throws IOException { int dA; while ((dA = sA.read()) != -1) { int dB = sB.read(); if (dA != dB) return false; } return sB.read() == -1; } /** * 将一段文本全部写入一个writer。 * <p> * <b style=color:red>注意</b>,它并不会关闭输出流 * * @param writer * * @param cs * 文本 * @throws IOException */ public static void write(Writer writer, CharSequence cs) throws IOException { if (null != cs && null != writer) { writer.write(cs.toString()); writer.flush(); } } /** * 将一段文本全部写入一个writer。 * <p> * <b style=color:red>注意</b>,它会关闭输出流 * * @param writer * 输出流 * @param cs * 文本 */ public static void writeAndClose(Writer writer, CharSequence cs) throws IOException { try { write(writer, cs); } finally { safeClose(writer); } } /** * 将输出流写入一个输出流。块大小为 8192 * <p> * <b style=color:red>注意</b>,它并不会关闭输入/出流 * * @param ops * 输出流 * @param ins * 输入流 * * @return 写入的字节数 * @throws IOException */ public static int write(OutputStream ops, InputStream ins) throws IOException { return write(ops, ins, BUF_SIZE); } /** * 将输出流写入一个输出流。 * <p> * <b style=color:red>注意</b>,它并不会关闭输入/出流 * * @param ops * 输出流 * @param ins * 输入流 * @param bufferSize * 缓冲块大小 * * @return 写入的字节数 * * @throws IOException */ public static int write(OutputStream ops, InputStream ins, int bufferSize) throws IOException { if (null == ops || null == ins) return 0; byte[] buf = new byte[bufferSize]; int len; int re = 0; while (-1 != (len = ins.read(buf))) { re += len; ops.write(buf, 0, len); } ops.flush(); return re; } /** * 将输出流写入一个输出流。块大小为 8192 * <p> * <b style=color:red>注意</b>,它会关闭输入/出流 * * @param ops * 输出流 * @param ins * 输入流 * @return 写入的字节数 */ public static int writeAndClose(OutputStream ops, InputStream ins) throws IOException { try { return write(ops, ins); } finally { safeClose(ops); safeClose(ins); } } /** * 将文本输出流写入一个文本输出流。块大小为 8192 * <p> * <b style=color:red>注意</b>,它并不会关闭输入/出流 * * @param writer * 输出流 * @param reader * 输入流 * @throws IOException */ public static void write(Writer writer, Reader reader) throws IOException { if (null == writer || null == reader) return; char[] cbuf = new char[BUF_SIZE]; int len; while (-1 != (len = reader.read(cbuf))) { writer.write(cbuf, 0, len); } } /** * 将文本输出流写入一个文本输出流。块大小为 8192 * <p> * <b style=color:red>注意</b>,它会关闭输入/出流 * * @param writer * 输出流 * @param reader * 输入流 */ public static void writeAndClose(Writer writer, Reader reader) throws IOException { try { write(writer, reader); } finally { safeClose(writer); safeClose(reader); } } /** * 将一个字节数组写入一个输出流。 * <p> * <b style=color:red>注意</b>,它并不会关闭输出流 * * @param ops * 输出流 * @param bytes * 字节数组 * @throws IOException */ public static void write(OutputStream ops, byte[] bytes) throws IOException { if (null == ops || null == bytes || bytes.length == 0) return; ops.write(bytes); } /** * 将一个字节数组写入一个输出流。 * <p> * <b style=color:red>注意</b>,它会关闭输出流 * * @param ops * 输出流 * @param bytes * 字节数组 */ public static void writeAndClose(OutputStream ops, byte[] bytes) throws IOException { try { write(ops, bytes); } finally { safeClose(ops); } } /** * 从一个文本流中读取全部内容并返回 * <p> * <b style=color:red>注意</b>,它并不会关闭输出流 * * @param reader * 文本输出流 * @return 文本内容 * @throws IOException */ public static StringBuilder read(Reader reader) throws IOException { StringBuilder sb = new StringBuilder(); char[] cbuf = new char[BUF_SIZE]; int len; while (-1 != (len = reader.read(cbuf))) { sb.append(cbuf, 0, len); } return sb; } /** * 从一个文本流中读取全部内容并返回 * <p> * <b style=color:red>注意</b>,它会关闭输入流 * * @param reader * 文本输入流 * @return 文本内容 * @throws IOException */ public static String readAndClose(Reader reader) throws IOException { try { return read(reader).toString(); } finally { safeClose(reader); } } /** * 读取一个输入流中所有的字节 * * @param ins * 输入流,必须支持 available() * @return 一个字节数组 * @throws IOException */ public static byte[] readBytes(InputStream ins) throws IOException { byte[] bytes = new byte[ins.available()]; ins.read(bytes); return bytes; } /** * 读取一个输入流中所有的字节,并关闭输入流 * * @param ins * 输入流,必须支持 available() * @return 一个字节数组 * @throws IOException */ public static byte[] readBytesAndClose(InputStream ins) throws IOException { byte[] bytes = null; try { bytes = readBytes(ins); } finally { StreamUtils.safeClose(ins); } return bytes; } /** * 关闭一个可关闭对象,可以接受 null。如果成功关闭,返回 true,发生异常 返回 false * * @param cb * 可关闭对象 * @return 是否成功关闭 */ public static boolean safeClose(Closeable cb) { if (null != cb) try { cb.close(); } catch (IOException e) { return false; } return true; } public static void safeFlush(Flushable fa) { if (null != fa) try { fa.flush(); } catch (IOException e) {} } /** * 为一个输入流包裹一个缓冲流。如果这个输入流本身就是缓冲流,则直接返回 * * @param ins * 输入流。 * @return 缓冲输入流 */ public static BufferedInputStream buff(InputStream ins) { if (ins instanceof BufferedInputStream) return (BufferedInputStream) ins; return new BufferedInputStream(ins); } /** * 为一个输出流包裹一个缓冲流。如果这个输出流本身就是缓冲流,则直接返回 * * @param ops * 输出流。 * @return 缓冲输出流 */ public static BufferedOutputStream buff(OutputStream ops) { if (ops instanceof BufferedOutputStream) return (BufferedOutputStream) ops; return new BufferedOutputStream(ops); } /** * 为一个文本输入流包裹一个缓冲流。如果这个输入流本身就是缓冲流,则直接返回 * * @param reader * 文本输入流。 * @return 缓冲文本输入流 */ public static BufferedReader buffr(Reader reader) { if (reader instanceof BufferedReader) return (BufferedReader) reader; return new BufferedReader(reader); } /** * 为一个文本输出流包裹一个缓冲流。如果这个文本输出流本身就是缓冲流,则直接返回 * * @param ops * 文本输出流。 * @return 缓冲文本输出流 */ public static BufferedWriter buffw(Writer ops) { if (ops instanceof BufferedWriter) return (BufferedWriter) ops; return new BufferedWriter(ops); } /** * 根据一个文件路径建立一个输入流 * * @param path * 文件路径 * @return 输入流 */ public static InputStream fileIn(String path) { InputStream ins = FileUtils.findFileAsStream(path); if (null == ins) { File f = FileUtils.findFile(path); if (null != f) try { ins = StreamUtils._input(f); } catch (IOException e) {} } return buff(ins); } /** * 根据一个文件路径建立一个输入流 * * @param file * 文件 * @return 输入流 */ public static InputStream fileIn(File file) throws IOException { return buff(StreamUtils._input(file)); } /** * 根据一个文件路径建立一个 UTF-8文本输入流 <b>警告!! 本方法会预先读取3个字节以判断该文件是否存在BOM头</b> * <p/> * <b>警告!! 如果存在BOM头,则自动跳过</b> * <p/> * * @param path * 文件路径 * @return 文本输入流 * @throws IOException * @throws UnsupportedEncodingException */ public static Reader fileInr(String path) throws UnsupportedEncodingException, IOException { return utf8r(fileIn(path)); } /** * 根据一个文件路径建立一个 UTF-8 文本输入流 <b>警告!! 本方法会预先读取3个字节以判断该文件是否存在BOM头</b> * <p/> * <b>警告!! 如果存在BOM头,则自动跳过</b> * <p/> * * @param file * 文件 * @return 文本输入流 * @throws IOException * @throws UnsupportedEncodingException */ public static Reader fileInr(File file) throws UnsupportedEncodingException, IOException { return utf8r(fileIn(file)); } private static final byte[] UTF_BOM = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}; /** * 判断并移除UTF-8的BOM头 */ public static InputStream utf8filte(InputStream in) throws IOException { if (in.available() == -1) return in; PushbackInputStream pis = new PushbackInputStream(in, 3); byte[] header = new byte[3]; int len = pis.read(header, 0, 3); if (len < 1) return in; if (header[0] != UTF_BOM[0] || header[1] != UTF_BOM[1] || header[2] != UTF_BOM[2]) { pis.unread(header, 0, len); } return pis; } /** * 根据一个文件路径建立一个输出流 * * @param path * 文件路径 * @return 输出流 * @throws FileNotFoundException */ public static OutputStream fileOut(String path) throws FileNotFoundException { return fileOut(FileUtils.findFile(path)); } /** * 根据一个文件建立一个输出流 * * @param file * 文件 * @return 输出流 */ public static OutputStream fileOut(File file) throws FileNotFoundException { return buff(new FileOutputStream(file)); } /** * 根据一个文件路径建立一个 UTF-8 文本输出流 * * @param path * 文件路径 * @return 文本输出流 * @throws IOException * @throws FileNotFoundException */ public static Writer fileOutw(String path) throws FileNotFoundException, IOException { return fileOutw(FileUtils.findFile(path)); } /** * 根据一个文件建立一个 UTF-8 文本输出流 * * @param file * 文件 * @return 输出流 * @throws IOException * @throws FileNotFoundException */ public static Writer fileOutw(File file) throws FileNotFoundException, IOException { return utf8w(fileOut(file)); } public static Reader utf8r(InputStream is) throws UnsupportedEncodingException, IOException { return new InputStreamReader(utf8filte(is), "UTF-8"); } public static Writer utf8w(OutputStream os) throws IOException { return new OutputStreamWriter(os, "UTF-8"); } public static InputStream wrap(byte[] bytes) { return new ByteArrayInputStream(bytes); } /** * 获取File对象输入流,即使在Jar文件中一样工作良好!! <b>强烈推荐</b> * */ protected static InputStream _input(File file) throws IOException { if (file.exists()) return new FileInputStream(file); if (ClassScanner.isInJar(file)) { Resource res = ClassScanner.makeJarResource(file); if (res != null) return res.getInputStream(); } return null; } public static void appendWriteAndClose(File f, String text) throws IOException { FileWriter fw = new FileWriter(f, true); fw.write(text); fw.close(); } }