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
* 【core 】 CollUtil.addAll增加判空pr#228@Gitee
* 【core 】 修正DateUtil.betweenXXX注释错误issue#I28XGW@Gitee
* 【core 】 增加NioUtil
### Bug修复
* 【cache 】 修复Cache中get重复misCount计数问题issue#1281@Github
@@ -29,6 +30,7 @@
* 【db 】 修复表名包含点导致的问题issue#1300@Github
* 【poi 】 修复xdr:row标签导致的问题issue#1297@Github
* 【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.Serializable;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
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;
import java.util.Collection;
import java.util.Objects;
@@ -48,25 +43,7 @@ import java.util.zip.Checksum;
*
* @author xiaoleilu
*/
public class IoUtil {
/**
* 默认缓存大小 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;
public class IoUtil extends NioUtil{
// -------------------------------------------------------------------------------------- Copy start
@@ -195,21 +172,6 @@ public class IoUtil {
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
*
@@ -227,79 +189,13 @@ public class IoUtil {
try {
inChannel = in.getChannel();
outChannel = out.getChannel();
return inChannel.transferTo(0, inChannel.size(), outChannel);
} catch (IOException e) {
throw new IORuntimeException(e);
return copy(inChannel, outChannel);
} finally {
close(outChannel);
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
// -------------------------------------------------------------------------------------- getReader and getWriter start
@@ -455,22 +351,7 @@ public class IoUtil {
* @throws IORuntimeException IO异常
*/
public static String read(InputStream in, Charset charset) throws IORuntimeException {
FastByteArrayOutputStream out = read(in);
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);
return StrUtil.str(readBytes(in), charset);
}
/**
@@ -486,19 +367,6 @@ public class IoUtil {
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
*
@@ -513,7 +381,7 @@ public class IoUtil {
/**
* 从{@link Reader}中读取String
*
* @param reader {@link Reader}
* @param reader {@link Reader}
* @param isClose 是否关闭{@link Reader}
* @return String
* @throws IORuntimeException IO异常
@@ -527,55 +395,14 @@ public class IoUtil {
}
} catch (IOException e) {
throw new IORuntimeException(e);
} finally{
if(isClose){
} finally {
if (isClose) {
IoUtil.close(reader);
}
}
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读取完毕后关闭流
*
@@ -597,12 +424,19 @@ public class IoUtil {
* @since 5.0.4
*/
public static byte[] readBytes(InputStream in, boolean isCloseStream) throws IORuntimeException {
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
copy(in, out);
if (isCloseStream) {
close(in);
final InputStream availableStream = toAvailableStream(in);
try{
final int available = availableStream.available();
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);
}
/**
* 将指定{@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[]写到流中
*
@@ -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>
* 判断对象如果实现了{@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 {
channel.connect();
in = channel.getInputStream();
return IoUtil.read(in, CharsetUtil.CHARSET_UTF_8);
return IoUtil.read(in, charset);
} catch (IOException e) {
throw new IORuntimeException(e);
} catch (JSchException e) {
@@ -461,7 +461,6 @@ public class JschUtil {
shell.setPty(true);
OutputStream out = null;
InputStream in = null;
final StringBuilder result = StrUtil.builder();
try {
out = shell.getOutputStream();
in = shell.getInputStream();
@@ -469,9 +468,7 @@ public class JschUtil {
out.write(StrUtil.bytes(cmd, charset));
out.flush();
while (in.available() > 0) {
result.append(IoUtil.read(in, charset));
}
return IoUtil.read(in, charset);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
@@ -479,7 +476,6 @@ public class JschUtil {
IoUtil.close(in);
close(shell);
}
return result.toString();
}
/**