add NioUtil

This commit is contained in:
Looly
2020-12-11 01:47:51 +08:00
parent b44dc1bb98
commit 4f6155c45a
4 changed files with 285 additions and 206 deletions

View File

@@ -18,6 +18,7 @@
* 【poi 】 Excel07SaxReader拆分出SheetDataSaxHandler * 【poi 】 Excel07SaxReader拆分出SheetDataSaxHandler
* 【core 】 CollUtil.addAll增加判空pr#228@Gitee * 【core 】 CollUtil.addAll增加判空pr#228@Gitee
* 【core 】 修正DateUtil.betweenXXX注释错误issue#I28XGW@Gitee * 【core 】 修正DateUtil.betweenXXX注释错误issue#I28XGW@Gitee
* 【core 】 增加NioUtil
### Bug修复 ### Bug修复
* 【cache 】 修复Cache中get重复misCount计数问题issue#1281@Github * 【cache 】 修复Cache中get重复misCount计数问题issue#1281@Github
@@ -29,6 +30,7 @@
* 【db 】 修复表名包含点导致的问题issue#1300@Github * 【db 】 修复表名包含点导致的问题issue#1300@Github
* 【poi 】 修复xdr:row标签导致的问题issue#1297@Github * 【poi 】 修复xdr:row标签导致的问题issue#1297@Github
* 【core 】 修复FileUtil.loopFiles使用FileFilter无效问题issue#I28V48@Gitee * 【core 】 修复FileUtil.loopFiles使用FileFilter无效问题issue#I28V48@Gitee
* 【extra 】 修复JschUtil.execByShell返回空的问题issue#1067@Github
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------

View File

@@ -28,13 +28,8 @@ import java.io.PushbackReader;
import java.io.Reader; import java.io.Reader;
import java.io.Serializable; import java.io.Serializable;
import java.io.Writer; import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Collection; import java.util.Collection;
import java.util.Objects; import java.util.Objects;
@@ -48,25 +43,7 @@ import java.util.zip.Checksum;
* *
* @author xiaoleilu * @author xiaoleilu
*/ */
public class IoUtil { public class IoUtil extends NioUtil{
/**
* 默认缓存大小 8192
*/
public static final int DEFAULT_BUFFER_SIZE = 2 << 12;
/**
* 默认中等缓存大小 16384
*/
public static final int DEFAULT_MIDDLE_BUFFER_SIZE = 2 << 13;
/**
* 默认大缓存大小 32768
*/
public static final int DEFAULT_LARGE_BUFFER_SIZE = 2 << 14;
/**
* 数据流末尾
*/
public static final int EOF = -1;
// -------------------------------------------------------------------------------------- Copy start // -------------------------------------------------------------------------------------- Copy start
@@ -195,21 +172,6 @@ public class IoUtil {
return size; return size;
} }
/**
* 拷贝流 thanks to: https://github.com/venusdrogon/feilong-io/blob/master/src/main/java/com/feilong/io/IOWriteUtil.java<br>
* 本方法不会关闭流
*
* @param in 输入流
* @param out 输出流
* @param bufferSize 缓存大小
* @param streamProgress 进度条
* @return 传输的byte数
* @throws IORuntimeException IO异常
*/
public static long copyByNIO(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
return copy(Channels.newChannel(in), Channels.newChannel(out), bufferSize, streamProgress);
}
/** /**
* 拷贝文件流使用NIO * 拷贝文件流使用NIO
* *
@@ -227,79 +189,13 @@ public class IoUtil {
try { try {
inChannel = in.getChannel(); inChannel = in.getChannel();
outChannel = out.getChannel(); outChannel = out.getChannel();
return inChannel.transferTo(0, inChannel.size(), outChannel); return copy(inChannel, outChannel);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally { } finally {
close(outChannel); close(outChannel);
close(inChannel); close(inChannel);
} }
} }
/**
* 拷贝流使用NIO不会关闭流
*
* @param in {@link ReadableByteChannel}
* @param out {@link WritableByteChannel}
* @return 拷贝的字节数
* @throws IORuntimeException IO异常
* @since 4.5.0
*/
public static long copy(ReadableByteChannel in, WritableByteChannel out) throws IORuntimeException {
return copy(in, out, DEFAULT_BUFFER_SIZE);
}
/**
* 拷贝流使用NIO不会关闭流
*
* @param in {@link ReadableByteChannel}
* @param out {@link WritableByteChannel}
* @param bufferSize 缓冲大小如果小于等于0使用默认
* @return 拷贝的字节数
* @throws IORuntimeException IO异常
* @since 4.5.0
*/
public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize) throws IORuntimeException {
return copy(in, out, bufferSize, null);
}
/**
* 拷贝流使用NIO不会关闭流
*
* @param in {@link ReadableByteChannel}
* @param out {@link WritableByteChannel}
* @param bufferSize 缓冲大小如果小于等于0使用默认
* @param streamProgress {@link StreamProgress}进度处理器
* @return 拷贝的字节数
* @throws IORuntimeException IO异常
*/
public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
Assert.notNull(in, "InputStream is null !");
Assert.notNull(out, "OutputStream is null !");
ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize <= 0 ? DEFAULT_BUFFER_SIZE : bufferSize);
long size = 0;
if (null != streamProgress) {
streamProgress.start();
}
try {
while (in.read(byteBuffer) != EOF) {
byteBuffer.flip();// 写转读
size += out.write(byteBuffer);
byteBuffer.clear();
if (null != streamProgress) {
streamProgress.progress(size);
}
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
if (null != streamProgress) {
streamProgress.finish();
}
return size;
}
// -------------------------------------------------------------------------------------- Copy end // -------------------------------------------------------------------------------------- Copy end
// -------------------------------------------------------------------------------------- getReader and getWriter start // -------------------------------------------------------------------------------------- getReader and getWriter start
@@ -455,22 +351,7 @@ public class IoUtil {
* @throws IORuntimeException IO异常 * @throws IORuntimeException IO异常
*/ */
public static String read(InputStream in, Charset charset) throws IORuntimeException { public static String read(InputStream in, Charset charset) throws IORuntimeException {
FastByteArrayOutputStream out = read(in); return StrUtil.str(readBytes(in), charset);
return null == charset ? out.toString() : out.toString(charset);
}
/**
* 从流中读取内容,读取完毕后并不关闭流
*
* @param channel 可读通道,读取完毕后并不关闭通道
* @param charset 字符集
* @return 内容
* @throws IORuntimeException IO异常
* @since 4.5.0
*/
public static String read(ReadableByteChannel channel, Charset charset) throws IORuntimeException {
FastByteArrayOutputStream out = read(channel);
return null == charset ? out.toString() : out.toString(charset);
} }
/** /**
@@ -486,19 +367,6 @@ public class IoUtil {
return out; return out;
} }
/**
* 从流中读取内容,读到输出流中
*
* @param channel 可读通道,读取完毕后并不关闭通道
* @return 输出流
* @throws IORuntimeException IO异常
*/
public static FastByteArrayOutputStream read(ReadableByteChannel channel) throws IORuntimeException {
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
copy(channel, Channels.newChannel(out));
return out;
}
/** /**
* 从Reader中读取String读取完毕后关闭Reader * 从Reader中读取String读取完毕后关闭Reader
* *
@@ -513,7 +381,7 @@ public class IoUtil {
/** /**
* 从{@link Reader}中读取String * 从{@link Reader}中读取String
* *
* @param reader {@link Reader} * @param reader {@link Reader}
* @param isClose 是否关闭{@link Reader} * @param isClose 是否关闭{@link Reader}
* @return String * @return String
* @throws IORuntimeException IO异常 * @throws IORuntimeException IO异常
@@ -527,55 +395,14 @@ public class IoUtil {
} }
} catch (IOException e) { } catch (IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} finally{ } finally {
if(isClose){ if (isClose) {
IoUtil.close(reader); IoUtil.close(reader);
} }
} }
return builder.toString(); return builder.toString();
} }
/**
* 从FileChannel中读取UTF-8编码内容
*
* @param fileChannel 文件管道
* @return 内容
* @throws IORuntimeException IO异常
*/
public static String readUtf8(FileChannel fileChannel) throws IORuntimeException {
return read(fileChannel, CharsetUtil.CHARSET_UTF_8);
}
/**
* 从FileChannel中读取内容读取完毕后并不关闭Channel
*
* @param fileChannel 文件管道
* @param charsetName 字符集
* @return 内容
* @throws IORuntimeException IO异常
*/
public static String read(FileChannel fileChannel, String charsetName) throws IORuntimeException {
return read(fileChannel, CharsetUtil.charset(charsetName));
}
/**
* 从FileChannel中读取内容
*
* @param fileChannel 文件管道
* @param charset 字符集
* @return 内容
* @throws IORuntimeException IO异常
*/
public static String read(FileChannel fileChannel, Charset charset) throws IORuntimeException {
MappedByteBuffer buffer;
try {
buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).load();
} catch (IOException e) {
throw new IORuntimeException(e);
}
return StrUtil.str(buffer, charset);
}
/** /**
* 从流中读取bytes读取完毕后关闭流 * 从流中读取bytes读取完毕后关闭流
* *
@@ -597,12 +424,19 @@ public class IoUtil {
* @since 5.0.4 * @since 5.0.4
*/ */
public static byte[] readBytes(InputStream in, boolean isCloseStream) throws IORuntimeException { public static byte[] readBytes(InputStream in, boolean isCloseStream) throws IORuntimeException {
final FastByteArrayOutputStream out = new FastByteArrayOutputStream(); final InputStream availableStream = toAvailableStream(in);
copy(in, out); try{
if (isCloseStream) { final int available = availableStream.available();
close(in); if(available > 0){
byte[] result = new byte[available];
//noinspection ResultOfMethodCallIgnored
availableStream.read(result);
return result;
}
} catch (IOException e){
throw new IORuntimeException(e);
} }
return out.toByteArray(); return new byte[0];
} }
/** /**
@@ -966,6 +800,42 @@ public class IoUtil {
return (in instanceof PushbackInputStream) ? (PushbackInputStream) in : new PushbackInputStream(in, pushBackSize); return (in instanceof PushbackInputStream) ? (PushbackInputStream) in : new PushbackInputStream(in, pushBackSize);
} }
/**
* 将指定{@link InputStream} 转换为{@link InputStream#available()}方法可用的流。<br>
* 在Socket通信流中服务端未返回数据情况下{@link InputStream#available()}方法始终为{@code 0}<br>
* 因此,在读取前需要调用{@link InputStream#read()}读取一个字节(未返回会阻塞),一旦读取到了,{@link InputStream#available()}方法就正常了。<br>
* 此方法返回对象的规则为:
*
* <ul>
* <li>FileInputStream 返回原对象因为文件流的available方法本身可用</li>
* <li>其它InputStream 返回PushbackInputStream</li>
* </ul>
*
* @param in 被转换的流
* @return 转换后的流,可能为{@link PushbackInputStream}
* @since 5.5.3
*/
public static InputStream toAvailableStream(InputStream in) {
if(in instanceof FileInputStream){
// FileInputStream本身支持available方法。
return in;
}
final PushbackInputStream pushbackInputStream = toPushbackStream(in, 1);
try {
final int available = pushbackInputStream.available();
if (available <= 0) {
//此操作会阻塞,直到有数据被读到
int b = pushbackInputStream.read();
pushbackInputStream.unread(b);
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
return pushbackInputStream;
}
/** /**
* 将byte[]写到流中 * 将byte[]写到流中
* *
@@ -1113,22 +983,6 @@ public class IoUtil {
} }
} }
/**
* 关闭<br>
* 关闭失败不会抛出异常
*
* @param closeable 被关闭的对象
*/
public static void close(AutoCloseable closeable) {
if (null != closeable) {
try {
closeable.close();
} catch (Exception e) {
// 静默关闭
}
}
}
/** /**
* 尝试关闭指定对象<br> * 尝试关闭指定对象<br>
* 判断对象如果实现了{@link AutoCloseable},则调用之 * 判断对象如果实现了{@link AutoCloseable},则调用之

View File

@@ -0,0 +1,227 @@
package cn.hutool.core.io;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
/**
* NIO相关工具封装主要针对Channel读写、拷贝等封装
*
* @author looly
* @since 5.5.3
*/
public class NioUtil {
/**
* 默认缓存大小 8192
*/
public static final int DEFAULT_BUFFER_SIZE = 2 << 12;
/**
* 默认中等缓存大小 16384
*/
public static final int DEFAULT_MIDDLE_BUFFER_SIZE = 2 << 13;
/**
* 默认大缓存大小 32768
*/
public static final int DEFAULT_LARGE_BUFFER_SIZE = 2 << 14;
/**
* 数据流末尾
*/
public static final int EOF = -1;
/**
* 拷贝流 thanks to: https://github.com/venusdrogon/feilong-io/blob/master/src/main/java/com/feilong/io/IOWriteUtil.java<br>
* 本方法不会关闭流
*
* @param in 输入流
* @param out 输出流
* @param bufferSize 缓存大小
* @param streamProgress 进度条
* @return 传输的byte数
* @throws IORuntimeException IO异常
*/
public static long copyByNIO(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
return copy(Channels.newChannel(in), Channels.newChannel(out), bufferSize, streamProgress);
}
/**
* 拷贝文件Channel使用NIO拷贝后不会关闭channel
*
* @param inChannel {@link FileChannel}
* @param outChannel {@link FileChannel}
* @return 拷贝的字节数
* @throws IORuntimeException IO异常
* @since 5.5.3
*/
public static long copy(FileChannel inChannel, FileChannel outChannel) throws IORuntimeException {
Assert.notNull(inChannel, "In channel is null!");
Assert.notNull(outChannel, "Out channel is null!");
try {
return inChannel.transferTo(0, inChannel.size(), outChannel);
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 拷贝流使用NIO不会关闭channel
*
* @param in {@link ReadableByteChannel}
* @param out {@link WritableByteChannel}
* @return 拷贝的字节数
* @throws IORuntimeException IO异常
* @since 4.5.0
*/
public static long copy(ReadableByteChannel in, WritableByteChannel out) throws IORuntimeException {
return copy(in, out, DEFAULT_BUFFER_SIZE);
}
/**
* 拷贝流使用NIO不会关闭channel
*
* @param in {@link ReadableByteChannel}
* @param out {@link WritableByteChannel}
* @param bufferSize 缓冲大小如果小于等于0使用默认
* @return 拷贝的字节数
* @throws IORuntimeException IO异常
* @since 4.5.0
*/
public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize) throws IORuntimeException {
return copy(in, out, bufferSize, null);
}
/**
* 拷贝流使用NIO不会关闭channel
*
* @param in {@link ReadableByteChannel}
* @param out {@link WritableByteChannel}
* @param bufferSize 缓冲大小如果小于等于0使用默认
* @param streamProgress {@link StreamProgress}进度处理器
* @return 拷贝的字节数
* @throws IORuntimeException IO异常
*/
public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
Assert.notNull(in, "InputStream is null !");
Assert.notNull(out, "OutputStream is null !");
ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize <= 0 ? DEFAULT_BUFFER_SIZE : bufferSize);
long size = 0;
if (null != streamProgress) {
streamProgress.start();
}
try {
while (in.read(byteBuffer) != EOF) {
byteBuffer.flip();// 写转读
size += out.write(byteBuffer);
byteBuffer.clear();
if (null != streamProgress) {
streamProgress.progress(size);
}
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
if (null != streamProgress) {
streamProgress.finish();
}
return size;
}
/**
* 从流中读取内容,读取完毕后并不关闭流
*
* @param channel 可读通道,读取完毕后并不关闭通道
* @param charset 字符集
* @return 内容
* @throws IORuntimeException IO异常
* @since 4.5.0
*/
public static String read(ReadableByteChannel channel, Charset charset) throws IORuntimeException {
FastByteArrayOutputStream out = read(channel);
return null == charset ? out.toString() : out.toString(charset);
}
/**
* 从流中读取内容,读到输出流中
*
* @param channel 可读通道,读取完毕后并不关闭通道
* @return 输出流
* @throws IORuntimeException IO异常
*/
public static FastByteArrayOutputStream read(ReadableByteChannel channel) throws IORuntimeException {
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
copy(channel, Channels.newChannel(out));
return out;
}
/**
* 从FileChannel中读取UTF-8编码内容
*
* @param fileChannel 文件管道
* @return 内容
* @throws IORuntimeException IO异常
*/
public static String readUtf8(FileChannel fileChannel) throws IORuntimeException {
return read(fileChannel, CharsetUtil.CHARSET_UTF_8);
}
/**
* 从FileChannel中读取内容读取完毕后并不关闭Channel
*
* @param fileChannel 文件管道
* @param charsetName 字符集
* @return 内容
* @throws IORuntimeException IO异常
*/
public static String read(FileChannel fileChannel, String charsetName) throws IORuntimeException {
return read(fileChannel, CharsetUtil.charset(charsetName));
}
/**
* 从FileChannel中读取内容
*
* @param fileChannel 文件管道
* @param charset 字符集
* @return 内容
* @throws IORuntimeException IO异常
*/
public static String read(FileChannel fileChannel, Charset charset) throws IORuntimeException {
MappedByteBuffer buffer;
try {
buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).load();
} catch (IOException e) {
throw new IORuntimeException(e);
}
return StrUtil.str(buffer, charset);
}
/**
* 关闭<br>
* 关闭失败不会抛出异常
*
* @param closeable 被关闭的对象
*/
public static void close(AutoCloseable closeable) {
if (null != closeable) {
try {
closeable.close();
} catch (Exception e) {
// 静默关闭
}
}
}
}

View File

@@ -432,7 +432,7 @@ public class JschUtil {
try { try {
channel.connect(); channel.connect();
in = channel.getInputStream(); in = channel.getInputStream();
return IoUtil.read(in, CharsetUtil.CHARSET_UTF_8); return IoUtil.read(in, charset);
} catch (IOException e) { } catch (IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} catch (JSchException e) { } catch (JSchException e) {
@@ -461,7 +461,6 @@ public class JschUtil {
shell.setPty(true); shell.setPty(true);
OutputStream out = null; OutputStream out = null;
InputStream in = null; InputStream in = null;
final StringBuilder result = StrUtil.builder();
try { try {
out = shell.getOutputStream(); out = shell.getOutputStream();
in = shell.getInputStream(); in = shell.getInputStream();
@@ -469,9 +468,7 @@ public class JschUtil {
out.write(StrUtil.bytes(cmd, charset)); out.write(StrUtil.bytes(cmd, charset));
out.flush(); out.flush();
while (in.available() > 0) { return IoUtil.read(in, charset);
result.append(IoUtil.read(in, charset));
}
} catch (IOException e) { } catch (IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} finally { } finally {
@@ -479,7 +476,6 @@ public class JschUtil {
IoUtil.close(in); IoUtil.close(in);
close(shell); close(shell);
} }
return result.toString();
} }
/** /**