diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e902f422..8deccb205 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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)
-------------------------------------------------------------------------------------------------------------
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
index 82c941f72..6feba0a8f 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
@@ -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
- * 本方法不会关闭流
- *
- * @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()}方法可用的流。
+ * 在Socket通信流中,服务端未返回数据情况下{@link InputStream#available()}方法始终为{@code 0}
+ * 因此,在读取前需要调用{@link InputStream#read()}读取一个字节(未返回会阻塞),一旦读取到了,{@link InputStream#available()}方法就正常了。
+ * 此方法返回对象的规则为:
+ *
+ *