diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b7c1cd2c..dec1188f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,15 @@
# Changelog
+-------------------------------------------------------------------------------------------------------------
+
+# 5.5.4 (2020-12-16)
+
+### 新特性
+### Bug修复
+* 【core 】 修复IoUtil.readBytes的问题
+
+
-------------------------------------------------------------------------------------------------------------
# 5.5.3 (2020-12-11)
diff --git a/README-EN.md b/README-EN.md
index 472d6b799..de675a27d 100644
--- a/README-EN.md
+++ b/README-EN.md
@@ -125,19 +125,19 @@ Each module can be introduced individually, or all modules can be introduced by
* base64编码是用64(2的6次方)个ASCII字符来表示256(2的8次方)个ASCII字符,
* 也就是三位二进制数组经过编码后变为四位的ASCII字符显示,长度比原来增加1/3。
- *
- * @author Looly
*
+ * @author Looly
*/
public class Base64 {
// -------------------------------------------------------------------- encode
+
/**
* 编码为Base64,非URL安全的
- *
- * @param arr 被编码的数组
+ *
+ * @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
*/
@@ -34,8 +34,8 @@ public class Base64 {
/**
* 编码为Base64,URL安全的
- *
- * @param arr 被编码的数组
+ *
+ * @param arr 被编码的数组
* @param lineSep 在76个char之后是CRLF还是EOF
* @return 编码后的bytes
* @since 3.0.6
@@ -46,7 +46,7 @@ public class Base64 {
/**
* base64编码
- *
+ *
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
@@ -56,7 +56,7 @@ public class Base64 {
/**
* base64编码,URL安全
- *
+ *
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
@@ -67,8 +67,8 @@ public class Base64 {
/**
* base64编码
- *
- * @param source 被编码的base64字符串
+ *
+ * @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
@@ -79,7 +79,8 @@ public class Base64 {
/**
* base64编码,不进行padding(末尾不会填充'=')
*
- * @param source 被编码的base64字符串
+ * @param source 被编码的base64字符串
+ * @param charset 编码
* @return 被加密后的字符串
* @since 5.5.2
*/
@@ -89,8 +90,8 @@ public class Base64 {
/**
* base64编码,URL安全
- *
- * @param source 被编码的base64字符串
+ *
+ * @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @since 3.0.6
@@ -101,8 +102,8 @@ public class Base64 {
/**
* base64编码
- *
- * @param source 被编码的base64字符串
+ *
+ * @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
@@ -112,8 +113,8 @@ public class Base64 {
/**
* base64编码,URL安全的
- *
- * @param source 被编码的base64字符串
+ *
+ * @param source 被编码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
* @since 3.0.6
@@ -124,7 +125,7 @@ public class Base64 {
/**
* base64编码
- *
+ *
* @param source 被编码的base64字符串
* @return 被加密后的字符串
*/
@@ -145,7 +146,7 @@ public class Base64 {
/**
* base64编码,URL安全的
- *
+ *
* @param source 被编码的base64字符串
* @return 被加密后的字符串
* @since 3.0.6
@@ -156,7 +157,7 @@ public class Base64 {
/**
* base64编码
- *
+ *
* @param in 被编码base64的流(一般为图片流或者文件流)
* @return 被加密后的字符串
* @since 4.0.9
@@ -167,7 +168,7 @@ public class Base64 {
/**
* base64编码,URL安全的
- *
+ *
* @param in 被编码base64的流(一般为图片流或者文件流)
* @return 被加密后的字符串
* @since 4.0.9
@@ -178,7 +179,7 @@ public class Base64 {
/**
* base64编码
- *
+ *
* @param file 被编码base64的文件
* @return 被加密后的字符串
* @since 4.0.9
@@ -189,7 +190,7 @@ public class Base64 {
/**
* base64编码,URL安全的
- *
+ *
* @param file 被编码base64的文件
* @return 被加密后的字符串
* @since 4.0.9
@@ -200,11 +201,11 @@ public class Base64 {
/**
* 编码为Base64
- * 如果isMultiLine为true
,则每76个字符一个换行符,否则在一行显示
- *
- * @param arr 被编码的数组
+ * 如果isMultiLine为{@code true},则每76个字符一个换行符,否则在一行显示
+ *
+ * @param arr 被编码的数组
* @param isMultiLine 在76个char之后是CRLF还是EOF
- * @param isUrlSafe 是否使用URL安全字符,一般为false
+ * @param isUrlSafe 是否使用URL安全字符,一般为{@code false}
* @return 编码后的bytes
*/
public static byte[] encode(byte[] arr, boolean isMultiLine, boolean isUrlSafe) {
@@ -212,9 +213,10 @@ public class Base64 {
}
// -------------------------------------------------------------------- decode
+
/**
* base64解码
- *
+ *
* @param source 被解码的base64字符串
* @return 被加密后的字符串
* @since 4.3.2
@@ -225,7 +227,7 @@ public class Base64 {
/**
* base64解码
- *
+ *
* @param source 被解码的base64字符串
* @return 被加密后的字符串
*/
@@ -235,8 +237,8 @@ public class Base64 {
/**
* base64解码
- *
- * @param source 被解码的base64字符串
+ *
+ * @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
@@ -246,8 +248,8 @@ public class Base64 {
/**
* base64解码
- *
- * @param source 被解码的base64字符串
+ *
+ * @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
*/
@@ -257,8 +259,8 @@ public class Base64 {
/**
* base64解码
- *
- * @param base64 被解码的base64字符串
+ *
+ * @param base64 被解码的base64字符串
* @param destFile 目标文件
* @return 目标文件
* @since 4.0.9
@@ -269,9 +271,9 @@ public class Base64 {
/**
* base64解码
- *
- * @param base64 被解码的base64字符串
- * @param out 写出到的流
+ *
+ * @param base64 被解码的base64字符串
+ * @param out 写出到的流
* @param isCloseOut 是否关闭输出流
* @since 4.0.9
*/
@@ -281,7 +283,7 @@ public class Base64 {
/**
* base64解码
- *
+ *
* @param base64 被解码的base64字符串
* @return 被加密后的字符串
*/
@@ -291,11 +293,11 @@ public class Base64 {
/**
* base64解码
- *
- * @param source 被解码的base64字符串
+ *
+ * @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
- *@deprecated 编码参数无意义,作废
+ * @deprecated 编码参数无意义,作废
*/
@Deprecated
public static byte[] decode(CharSequence source, String charset) {
@@ -304,11 +306,11 @@ public class Base64 {
/**
* base64解码
- *
- * @param source 被解码的base64字符串
+ *
+ * @param source 被解码的base64字符串
* @param charset 字符集
* @return 被加密后的字符串
- *@deprecated 编码参数无意义,作废
+ * @deprecated 编码参数无意义,作废
*/
@Deprecated
public static byte[] decode(CharSequence source, Charset charset) {
@@ -317,7 +319,7 @@ public class Base64 {
/**
* 解码Base64
- *
+ *
* @param in 输入
* @return 解码后的bytes
*/
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 6feba0a8f..ce5bbcc82 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
@@ -43,7 +43,7 @@ import java.util.zip.Checksum;
*
* @author xiaoleilu
*/
-public class IoUtil extends NioUtil{
+public class IoUtil extends NioUtil {
// -------------------------------------------------------------------------------------- Copy start
@@ -330,7 +330,7 @@ public class IoUtil extends NioUtil{
}
/**
- * 从流中读取内容
+ * 从流中读取内容,读取完成后关闭流
*
* @param in 输入流
* @param charsetName 字符集
@@ -338,7 +338,7 @@ public class IoUtil extends NioUtil{
* @throws IORuntimeException IO异常
*/
public static String read(InputStream in, String charsetName) throws IORuntimeException {
- FastByteArrayOutputStream out = read(in);
+ final FastByteArrayOutputStream out = read(in);
return StrUtil.isBlank(charsetName) ? out.toString() : out.toString(charsetName);
}
@@ -362,8 +362,27 @@ public class IoUtil extends NioUtil{
* @throws IORuntimeException IO异常
*/
public static FastByteArrayOutputStream read(InputStream in) throws IORuntimeException {
+ return read(in, true);
+ }
+
+ /**
+ * 从流中读取内容,读到输出流中,读取完毕后并不关闭流
+ *
+ * @param in 输入流
+ * @param isClose 读取完毕后是否关闭流
+ * @return 输出流
+ * @throws IORuntimeException IO异常
+ * @since 5.5.3
+ */
+ public static FastByteArrayOutputStream read(InputStream in, boolean isClose) throws IORuntimeException {
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
- copy(in, out);
+ try {
+ copy(in, out);
+ } finally {
+ if (isClose) {
+ close(in);
+ }
+ }
return out;
}
@@ -417,26 +436,31 @@ public class IoUtil extends NioUtil{
/**
* 从流中读取bytes
*
- * @param in {@link InputStream}
- * @param isCloseStream 是否关闭输入流
+ * @param in {@link InputStream}
+ * @param isCLose 是否关闭输入流
* @return bytes
* @throws IORuntimeException IO异常
* @since 5.0.4
*/
- public static byte[] readBytes(InputStream in, boolean isCloseStream) throws IORuntimeException {
- 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;
+ public static byte[] readBytes(InputStream in, boolean isCLose) throws IORuntimeException {
+ if (in instanceof FileInputStream) {
+ // 文件流的长度是可预见的,此时直接读取效率更高
+ final byte[] result;
+ try {
+ final int available = in.available();
+ result = new byte[available];
+ final int readLength = in.read(result);
+ if (readLength != available) {
+ throw new IOException(StrUtil.format("File length is [{}] but read [{}]!", available, readLength));
+ }
+ } catch (IOException e) {
+ throw new IORuntimeException(e);
}
- } catch (IOException e){
- throw new IORuntimeException(e);
+ return result;
}
- return new byte[0];
+
+ // 未知bytes总量的流
+ return read(in, isCLose).toByteArray();
}
/**
@@ -804,6 +828,7 @@ public class IoUtil extends NioUtil{
* 将指定{@link InputStream} 转换为{@link InputStream#available()}方法可用的流。
* 在Socket通信流中,服务端未返回数据情况下{@link InputStream#available()}方法始终为{@code 0}
* 因此,在读取前需要调用{@link InputStream#read()}读取一个字节(未返回会阻塞),一旦读取到了,{@link InputStream#available()}方法就正常了。
+ * 需要注意的是,在网络流中,是按照块来传输的,所以 {@link InputStream#available()} 读取到的并非最终长度,而是此次块的长度。
* 此方法返回对象的规则为:
*
*