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();
}
}