mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add IoCopier
This commit is contained in:
parent
9dd7b0a904
commit
c54f2a154a
@ -15,6 +15,7 @@
|
|||||||
* 【socket 】 SocketUtil增加connection方法
|
* 【socket 】 SocketUtil增加connection方法
|
||||||
* 【extra 】 JschUtil增加bindPort重载方法(issue#I44UTH@Github)
|
* 【extra 】 JschUtil增加bindPort重载方法(issue#I44UTH@Github)
|
||||||
* 【core 】 DefaultTrustManager改为继承X509ExtendedTrustManager
|
* 【core 】 DefaultTrustManager改为继承X509ExtendedTrustManager
|
||||||
|
* 【core 】 增加IoCopier
|
||||||
|
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
* 【core 】 改进NumberChineseFormatter算法,补充完整单元测试,解决零问题
|
* 【core 】 改进NumberChineseFormatter算法,补充完整单元测试,解决零问题
|
||||||
|
@ -3,6 +3,8 @@ package cn.hutool.core.io;
|
|||||||
import cn.hutool.core.collection.LineIter;
|
import cn.hutool.core.collection.LineIter;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.exceptions.UtilException;
|
import cn.hutool.core.exceptions.UtilException;
|
||||||
|
import cn.hutool.core.io.copy.ReaderWriterCopier;
|
||||||
|
import cn.hutool.core.io.copy.StreamCopier;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.core.util.HexUtil;
|
import cn.hutool.core.util.HexUtil;
|
||||||
@ -86,28 +88,21 @@ public class IoUtil extends NioUtil {
|
|||||||
* @throws IORuntimeException IO异常
|
* @throws IORuntimeException IO异常
|
||||||
*/
|
*/
|
||||||
public static long copy(Reader reader, Writer writer, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
|
public static long copy(Reader reader, Writer writer, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
|
||||||
char[] buffer = new char[bufferSize];
|
return copy(reader, writer, bufferSize, -1, streamProgress);
|
||||||
long size = 0;
|
}
|
||||||
int readSize;
|
|
||||||
if (null != streamProgress) {
|
/**
|
||||||
streamProgress.start();
|
* 将Reader中的内容复制到Writer中,拷贝后不关闭Reader
|
||||||
}
|
*
|
||||||
try {
|
* @param reader Reader
|
||||||
while ((readSize = reader.read(buffer, 0, bufferSize)) != EOF) {
|
* @param writer Writer
|
||||||
writer.write(buffer, 0, readSize);
|
* @param bufferSize 缓存大小
|
||||||
size += readSize;
|
* @param streamProgress 进度处理器
|
||||||
writer.flush();
|
* @return 传输的byte数
|
||||||
if (null != streamProgress) {
|
* @throws IORuntimeException IO异常
|
||||||
streamProgress.progress(size);
|
*/
|
||||||
}
|
public static long copy(Reader reader, Writer writer, int bufferSize, int count, StreamProgress streamProgress) throws IORuntimeException {
|
||||||
}
|
return new ReaderWriterCopier(bufferSize, count, streamProgress).copy(reader, writer);
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IORuntimeException(e);
|
|
||||||
}
|
|
||||||
if (null != streamProgress) {
|
|
||||||
streamProgress.finish();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,33 +141,23 @@ public class IoUtil extends NioUtil {
|
|||||||
* @throws IORuntimeException IO异常
|
* @throws IORuntimeException IO异常
|
||||||
*/
|
*/
|
||||||
public static long copy(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
|
public static long copy(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
|
||||||
Assert.notNull(in, "InputStream is null !");
|
return copy(in, out, bufferSize, -1, streamProgress);
|
||||||
Assert.notNull(out, "OutputStream is null !");
|
}
|
||||||
if (bufferSize <= 0) {
|
|
||||||
bufferSize = DEFAULT_BUFFER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] buffer = new byte[bufferSize];
|
/**
|
||||||
if (null != streamProgress) {
|
* 拷贝流,拷贝后不关闭流
|
||||||
streamProgress.start();
|
*
|
||||||
}
|
* @param in 输入流
|
||||||
long size = 0;
|
* @param out 输出流
|
||||||
try {
|
* @param bufferSize 缓存大小
|
||||||
for (int readSize; (readSize = in.read(buffer)) != EOF; ) {
|
* @param count 总拷贝长度
|
||||||
out.write(buffer, 0, readSize);
|
* @param streamProgress 进度条
|
||||||
size += readSize;
|
* @return 传输的byte数
|
||||||
if (null != streamProgress) {
|
* @throws IORuntimeException IO异常
|
||||||
streamProgress.progress(size);
|
* @since 5.7.8
|
||||||
}
|
*/
|
||||||
}
|
public static long copy(InputStream in, OutputStream out, int bufferSize, int count, StreamProgress streamProgress) throws IORuntimeException {
|
||||||
out.flush();
|
return new StreamCopier(bufferSize, count, streamProgress).copy(in, out);
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IORuntimeException(e);
|
|
||||||
}
|
|
||||||
if (null != streamProgress) {
|
|
||||||
streamProgress.finish();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -390,14 +375,14 @@ public class IoUtil extends NioUtil {
|
|||||||
*/
|
*/
|
||||||
public static FastByteArrayOutputStream read(InputStream in, boolean isClose) throws IORuntimeException {
|
public static FastByteArrayOutputStream read(InputStream in, boolean isClose) throws IORuntimeException {
|
||||||
final FastByteArrayOutputStream out;
|
final FastByteArrayOutputStream out;
|
||||||
if(in instanceof FileInputStream){
|
if (in instanceof FileInputStream) {
|
||||||
// 文件流的长度是可预见的,此时直接读取效率更高
|
// 文件流的长度是可预见的,此时直接读取效率更高
|
||||||
try {
|
try {
|
||||||
out = new FastByteArrayOutputStream(in.available());
|
out = new FastByteArrayOutputStream(in.available());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
} else{
|
} else {
|
||||||
out = new FastByteArrayOutputStream();
|
out = new FastByteArrayOutputStream();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -434,7 +419,7 @@ public class IoUtil extends NioUtil {
|
|||||||
final CharBuffer buffer = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
|
final CharBuffer buffer = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
|
||||||
try {
|
try {
|
||||||
while (-1 != reader.read(buffer)) {
|
while (-1 != reader.read(buffer)) {
|
||||||
builder.append(buffer.flip().toString());
|
builder.append(buffer.flip());
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
@ -828,7 +813,7 @@ public class IoUtil extends NioUtil {
|
|||||||
/**
|
/**
|
||||||
* 转换为{@link BufferedInputStream}
|
* 转换为{@link BufferedInputStream}
|
||||||
*
|
*
|
||||||
* @param in {@link InputStream}
|
* @param in {@link InputStream}
|
||||||
* @param bufferSize buffer size
|
* @param bufferSize buffer size
|
||||||
* @return {@link BufferedInputStream}
|
* @return {@link BufferedInputStream}
|
||||||
* @since 5.6.1
|
* @since 5.6.1
|
||||||
@ -853,7 +838,7 @@ public class IoUtil extends NioUtil {
|
|||||||
/**
|
/**
|
||||||
* 转换为{@link BufferedOutputStream}
|
* 转换为{@link BufferedOutputStream}
|
||||||
*
|
*
|
||||||
* @param out {@link OutputStream}
|
* @param out {@link OutputStream}
|
||||||
* @param bufferSize buffer size
|
* @param bufferSize buffer size
|
||||||
* @return {@link BufferedOutputStream}
|
* @return {@link BufferedOutputStream}
|
||||||
* @since 5.6.1
|
* @since 5.6.1
|
||||||
@ -878,7 +863,7 @@ public class IoUtil extends NioUtil {
|
|||||||
/**
|
/**
|
||||||
* 转换为{@link BufferedReader}
|
* 转换为{@link BufferedReader}
|
||||||
*
|
*
|
||||||
* @param reader {@link Reader}
|
* @param reader {@link Reader}
|
||||||
* @param bufferSize buffer size
|
* @param bufferSize buffer size
|
||||||
* @return {@link BufferedReader}
|
* @return {@link BufferedReader}
|
||||||
* @since 5.6.1
|
* @since 5.6.1
|
||||||
@ -903,7 +888,7 @@ public class IoUtil extends NioUtil {
|
|||||||
/**
|
/**
|
||||||
* 转换为{@link BufferedWriter}
|
* 转换为{@link BufferedWriter}
|
||||||
*
|
*
|
||||||
* @param writer {@link Writer}
|
* @param writer {@link Writer}
|
||||||
* @param bufferSize buffer size
|
* @param bufferSize buffer size
|
||||||
* @return {@link BufferedWriter}
|
* @return {@link BufferedWriter}
|
||||||
* @since 5.6.1
|
* @since 5.6.1
|
||||||
@ -1291,7 +1276,7 @@ public class IoUtil extends NioUtil {
|
|||||||
* while (it.hasNext()) {
|
* while (it.hasNext()) {
|
||||||
* String line = it.nextLine();
|
* String line = it.nextLine();
|
||||||
* // do something with line
|
* // do something with line
|
||||||
* }
|
* }
|
||||||
* } finally {
|
* } finally {
|
||||||
* it.close();
|
* it.close();
|
||||||
* }
|
* }
|
||||||
@ -1301,7 +1286,7 @@ public class IoUtil extends NioUtil {
|
|||||||
* @return {@link LineIter}
|
* @return {@link LineIter}
|
||||||
* @since 5.6.1
|
* @since 5.6.1
|
||||||
*/
|
*/
|
||||||
public static LineIter lineIter(Reader reader){
|
public static LineIter lineIter(Reader reader) {
|
||||||
return new LineIter(reader);
|
return new LineIter(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1314,18 +1299,18 @@ public class IoUtil extends NioUtil {
|
|||||||
* while (it.hasNext()) {
|
* while (it.hasNext()) {
|
||||||
* String line = it.nextLine();
|
* String line = it.nextLine();
|
||||||
* // do something with line
|
* // do something with line
|
||||||
* }
|
* }
|
||||||
* } finally {
|
* } finally {
|
||||||
* it.close();
|
* it.close();
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param in {@link InputStream}
|
* @param in {@link InputStream}
|
||||||
* @param charset 编码
|
* @param charset 编码
|
||||||
* @return {@link LineIter}
|
* @return {@link LineIter}
|
||||||
* @since 5.6.1
|
* @since 5.6.1
|
||||||
*/
|
*/
|
||||||
public static LineIter lineIter(InputStream in, Charset charset){
|
public static LineIter lineIter(InputStream in, Charset charset) {
|
||||||
return new LineIter(in, charset);
|
return new LineIter(in, charset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package cn.hutool.core.io;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream进度条
|
* Stream进度条
|
||||||
* @author Looly
|
|
||||||
*
|
*
|
||||||
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public interface StreamProgress {
|
public interface StreamProgress {
|
||||||
|
|
||||||
@ -14,6 +14,7 @@ public interface StreamProgress {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 进行中
|
* 进行中
|
||||||
|
*
|
||||||
* @param progressSize 已经进行的大小
|
* @param progressSize 已经进行的大小
|
||||||
*/
|
*/
|
||||||
void progress(long progressSize);
|
void progress(long progressSize);
|
||||||
|
60
hutool-core/src/main/java/cn/hutool/core/io/copy/IoCopier.java
Executable file
60
hutool-core/src/main/java/cn/hutool/core/io/copy/IoCopier.java
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
package cn.hutool.core.io.copy;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.io.StreamProgress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IO拷贝抽象,可自定义包括缓存、进度条等信息<br>
|
||||||
|
* 此对象非线程安全
|
||||||
|
*
|
||||||
|
* @param <S> 拷贝源类型,如InputStream、Reader等
|
||||||
|
* @param <T> 拷贝目标类型,如OutputStream、Writer等
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.8
|
||||||
|
*/
|
||||||
|
public abstract class IoCopier<S, T> {
|
||||||
|
|
||||||
|
protected final int bufferSize;
|
||||||
|
/**
|
||||||
|
* 拷贝总数
|
||||||
|
*/
|
||||||
|
protected final long count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进度条
|
||||||
|
*/
|
||||||
|
protected StreamProgress progress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bufferSize 缓存大小,< 0 表示默认{@link IoUtil#DEFAULT_BUFFER_SIZE}
|
||||||
|
* @param count 拷贝总数
|
||||||
|
* @param progress 进度条
|
||||||
|
*/
|
||||||
|
public IoCopier(int bufferSize, long count, StreamProgress progress) {
|
||||||
|
this.bufferSize = bufferSize > 0 ? bufferSize : IoUtil.DEFAULT_BUFFER_SIZE;
|
||||||
|
this.count = count;
|
||||||
|
this.progress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行拷贝
|
||||||
|
*
|
||||||
|
* @param source 拷贝源,如InputStream、Reader等
|
||||||
|
* @param target 拷贝目标,如OutputStream、Writer等
|
||||||
|
* @return 拷贝的实际长度
|
||||||
|
*/
|
||||||
|
public abstract long copy(S source, T target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存大小,取默认缓存和目标长度最小值
|
||||||
|
* @return 缓存大小
|
||||||
|
*/
|
||||||
|
protected int bufferSize(long count) {
|
||||||
|
if(count < 0){
|
||||||
|
count = Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return Math.min(this.bufferSize, (int)count);
|
||||||
|
}
|
||||||
|
}
|
114
hutool-core/src/main/java/cn/hutool/core/io/copy/ReaderWriterCopier.java
Executable file
114
hutool-core/src/main/java/cn/hutool/core/io/copy/ReaderWriterCopier.java
Executable file
@ -0,0 +1,114 @@
|
|||||||
|
package cn.hutool.core.io.copy;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.io.StreamProgress;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Reader} 向 {@link Writer} 拷贝
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.8
|
||||||
|
*/
|
||||||
|
public class ReaderWriterCopier extends IoCopier<Reader, Writer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
public ReaderWriterCopier() {
|
||||||
|
this(IoUtil.DEFAULT_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bufferSize 缓存大小
|
||||||
|
*/
|
||||||
|
public ReaderWriterCopier(int bufferSize) {
|
||||||
|
this(bufferSize, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bufferSize 缓存大小
|
||||||
|
* @param count 拷贝总数
|
||||||
|
*/
|
||||||
|
public ReaderWriterCopier(int bufferSize, long count) {
|
||||||
|
this(bufferSize, count, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bufferSize 缓存大小
|
||||||
|
* @param count 拷贝总数
|
||||||
|
* @param progress 进度条
|
||||||
|
*/
|
||||||
|
public ReaderWriterCopier(int bufferSize, long count, StreamProgress progress) {
|
||||||
|
super(bufferSize, count, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long copy(Reader source, Writer target) {
|
||||||
|
Assert.notNull(source, "InputStream is null !");
|
||||||
|
Assert.notNull(target, "OutputStream is null !");
|
||||||
|
|
||||||
|
final StreamProgress progress = this.progress;
|
||||||
|
if (null != progress) {
|
||||||
|
progress.start();
|
||||||
|
}
|
||||||
|
final long size;
|
||||||
|
try {
|
||||||
|
size = doCopy(source, target, new char[bufferSize(this.count)], progress);
|
||||||
|
target.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != progress) {
|
||||||
|
progress.finish();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行拷贝,如果限制最大长度,则按照最大长度读取,否则一直读取直到遇到-1
|
||||||
|
*
|
||||||
|
* @param source {@link InputStream}
|
||||||
|
* @param target {@link OutputStream}
|
||||||
|
* @param buffer 缓存
|
||||||
|
* @param progress 进度条
|
||||||
|
* @return 拷贝总长度
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
private long doCopy(Reader source, Writer target, char[] buffer, StreamProgress progress) throws IOException {
|
||||||
|
long numToRead = this.count > 0 ? this.count : Long.MAX_VALUE;
|
||||||
|
long total = 0;
|
||||||
|
|
||||||
|
int read;
|
||||||
|
while (numToRead > 0) {
|
||||||
|
read = source.read(buffer, 0, bufferSize(numToRead));
|
||||||
|
if (read < 0) {
|
||||||
|
// 提前读取到末尾
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
target.write(buffer, 0, read);
|
||||||
|
|
||||||
|
numToRead -= read;
|
||||||
|
total += read;
|
||||||
|
if (null != progress) {
|
||||||
|
progress.progress(total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
}
|
112
hutool-core/src/main/java/cn/hutool/core/io/copy/StreamCopier.java
Executable file
112
hutool-core/src/main/java/cn/hutool/core/io/copy/StreamCopier.java
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
package cn.hutool.core.io.copy;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.io.StreamProgress;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link InputStream} 向 {@link OutputStream} 拷贝
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.8
|
||||||
|
*/
|
||||||
|
public class StreamCopier extends IoCopier<InputStream, OutputStream> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
public StreamCopier() {
|
||||||
|
this(IoUtil.DEFAULT_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bufferSize 缓存大小
|
||||||
|
*/
|
||||||
|
public StreamCopier(int bufferSize) {
|
||||||
|
this(bufferSize, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bufferSize 缓存大小
|
||||||
|
* @param count 拷贝总数
|
||||||
|
*/
|
||||||
|
public StreamCopier(int bufferSize, long count) {
|
||||||
|
this(bufferSize, count, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param bufferSize 缓存大小
|
||||||
|
* @param count 拷贝总数
|
||||||
|
* @param progress 进度条
|
||||||
|
*/
|
||||||
|
public StreamCopier(int bufferSize, long count, StreamProgress progress) {
|
||||||
|
super(bufferSize, count, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long copy(InputStream source, OutputStream target) {
|
||||||
|
Assert.notNull(source, "InputStream is null !");
|
||||||
|
Assert.notNull(target, "OutputStream is null !");
|
||||||
|
|
||||||
|
final StreamProgress progress = this.progress;
|
||||||
|
if (null != progress) {
|
||||||
|
progress.start();
|
||||||
|
}
|
||||||
|
final long size;
|
||||||
|
try {
|
||||||
|
size = doCopy(source, target, new byte[bufferSize(this.count)], progress);
|
||||||
|
target.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != progress) {
|
||||||
|
progress.finish();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行拷贝,如果限制最大长度,则按照最大长度读取,否则一直读取直到遇到-1
|
||||||
|
*
|
||||||
|
* @param source {@link InputStream}
|
||||||
|
* @param target {@link OutputStream}
|
||||||
|
* @param buffer 缓存
|
||||||
|
* @param progress 进度条
|
||||||
|
* @return 拷贝总长度
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
private long doCopy(InputStream source, OutputStream target, byte[] buffer, StreamProgress progress) throws IOException {
|
||||||
|
long numToRead = this.count > 0 ? this.count : Long.MAX_VALUE;
|
||||||
|
long total = 0;
|
||||||
|
|
||||||
|
int read;
|
||||||
|
while (numToRead > 0) {
|
||||||
|
read = source.read(buffer, 0, bufferSize(numToRead));
|
||||||
|
if (read < 0) {
|
||||||
|
// 提前读取到末尾
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
target.write(buffer, 0, read);
|
||||||
|
|
||||||
|
numToRead -= read;
|
||||||
|
total += read;
|
||||||
|
if (null != progress) {
|
||||||
|
progress.progress(total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
}
|
7
hutool-core/src/main/java/cn/hutool/core/io/copy/package-info.java
Executable file
7
hutool-core/src/main/java/cn/hutool/core/io/copy/package-info.java
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* IO流拷贝相关封装相关封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.8
|
||||||
|
*/
|
||||||
|
package cn.hutool.core.io.copy;
|
@ -78,8 +78,7 @@ public class Snowflake implements Serializable {
|
|||||||
* 构造,使用自动生成的工作节点ID和数据中心ID
|
* 构造,使用自动生成的工作节点ID和数据中心ID
|
||||||
*/
|
*/
|
||||||
public Snowflake() {
|
public Snowflake() {
|
||||||
this(IdUtil.getWorkerId(IdUtil.getDataCenterId(MAX_DATA_CENTER_ID), MAX_WORKER_ID)
|
this(IdUtil.getWorkerId(IdUtil.getDataCenterId(MAX_DATA_CENTER_ID), MAX_WORKER_ID));
|
||||||
, IdUtil.getDataCenterId(MAX_DATA_CENTER_ID));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +14,12 @@ import java.security.cert.X509Certificate;
|
|||||||
*/
|
*/
|
||||||
public class DefaultTrustManager extends X509ExtendedTrustManager {
|
public class DefaultTrustManager extends X509ExtendedTrustManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的全局单例默认信任管理器,,默认信任所有客户端和服务端证书
|
||||||
|
* @since 5.7.8
|
||||||
|
*/
|
||||||
|
public static DefaultTrustManager INSTANCE = new DefaultTrustManager();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -13,46 +13,24 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link SSLContext}构建器
|
* {@link SSLContext}构建器,可以自定义:<br>
|
||||||
|
* <ul>
|
||||||
|
* <li>协议(protocol),默认TLS</li>
|
||||||
|
* <li>{@link KeyManager},默认空</li>
|
||||||
|
* <li>{@link TrustManager},默认{@link DefaultTrustManager},即信任全部</li>
|
||||||
|
* <li>{@link SecureRandom}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* 构建后可获得{@link SSLContext},通过调用{@link SSLContext#getSocketFactory()}获取{@link javax.net.ssl.SSLSocketFactory}
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
* @since 5.5.2
|
* @since 5.5.2
|
||||||
*/
|
*/
|
||||||
public class SSLContextBuilder {
|
public class SSLContextBuilder implements SSLProtocols {
|
||||||
|
|
||||||
/**
|
|
||||||
* Supports some version of SSL; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String SSL = "SSL";
|
|
||||||
/**
|
|
||||||
* Supports SSL version 2 or later; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String SSLv2 = "SSLv2";
|
|
||||||
/**
|
|
||||||
* Supports SSL version 3; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String SSLv3 = "SSLv3";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supports some version of TLS; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String TLS = "TLS";
|
|
||||||
/**
|
|
||||||
* Supports RFC 2246: TLS version 1.0 ; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String TLSv1 = "TLSv1";
|
|
||||||
/**
|
|
||||||
* Supports RFC 4346: TLS version 1.1 ; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String TLSv11 = "TLSv1.1";
|
|
||||||
/**
|
|
||||||
* Supports RFC 5246: TLS version 1.2 ; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String TLSv12 = "TLSv1.2";
|
|
||||||
|
|
||||||
private String protocol = TLS;
|
private String protocol = TLS;
|
||||||
private KeyManager[] keyManagers;
|
private KeyManager[] keyManagers;
|
||||||
private TrustManager[] trustManagers = {new DefaultTrustManager()};
|
private TrustManager[] trustManagers = {DefaultTrustManager.INSTANCE};
|
||||||
private SecureRandom secureRandom = new SecureRandom();
|
private SecureRandom secureRandom = new SecureRandom();
|
||||||
|
|
||||||
|
|
||||||
@ -136,7 +114,7 @@ public class SSLContextBuilder {
|
|||||||
* @return {@link SSLContext}
|
* @return {@link SSLContext}
|
||||||
* @throws IORuntimeException 包装 GeneralSecurityException异常
|
* @throws IORuntimeException 包装 GeneralSecurityException异常
|
||||||
*/
|
*/
|
||||||
public SSLContext buildQuietly() throws IORuntimeException{
|
public SSLContext buildQuietly() throws IORuntimeException {
|
||||||
try {
|
try {
|
||||||
return build();
|
return build();
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
|
40
hutool-core/src/main/java/cn/hutool/core/net/SSLProtocols.java
Executable file
40
hutool-core/src/main/java/cn/hutool/core/net/SSLProtocols.java
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
package cn.hutool.core.net;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSL或TLS协议
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.8
|
||||||
|
*/
|
||||||
|
public interface SSLProtocols {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports some version of SSL; may support other versions
|
||||||
|
*/
|
||||||
|
String SSL = "SSL";
|
||||||
|
/**
|
||||||
|
* Supports SSL version 2 or later; may support other versions
|
||||||
|
*/
|
||||||
|
String SSLv2 = "SSLv2";
|
||||||
|
/**
|
||||||
|
* Supports SSL version 3; may support other versions
|
||||||
|
*/
|
||||||
|
String SSLv3 = "SSLv3";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports some version of TLS; may support other versions
|
||||||
|
*/
|
||||||
|
String TLS = "TLS";
|
||||||
|
/**
|
||||||
|
* Supports RFC 2246: TLS version 1.0 ; may support other versions
|
||||||
|
*/
|
||||||
|
String TLSv1 = "TLSv1";
|
||||||
|
/**
|
||||||
|
* Supports RFC 4346: TLS version 1.1 ; may support other versions
|
||||||
|
*/
|
||||||
|
String TLSv11 = "TLSv1.1";
|
||||||
|
/**
|
||||||
|
* Supports RFC 5246: TLS version 1.2 ; may support other versions
|
||||||
|
*/
|
||||||
|
String TLSv12 = "TLSv1.2";
|
||||||
|
}
|
@ -14,6 +14,18 @@ import javax.net.ssl.TrustManager;
|
|||||||
*/
|
*/
|
||||||
public class SSLUtil {
|
public class SSLUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link SSLContext},默认新人全部
|
||||||
|
*
|
||||||
|
* @param protocol SSL协议,例如TLS等
|
||||||
|
* @return {@link SSLContext}
|
||||||
|
* @throws IORuntimeException 包装 GeneralSecurityException异常
|
||||||
|
* @since 5.7.8
|
||||||
|
*/
|
||||||
|
public static SSLContext createSSLContext(String protocol) throws IORuntimeException{
|
||||||
|
return SSLContextBuilder.create().setProtocol(protocol).buildQuietly();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建{@link SSLContext}
|
* 创建{@link SSLContext}
|
||||||
*
|
*
|
||||||
|
@ -10,6 +10,7 @@ import cn.hutool.core.io.resource.MultiFileResource;
|
|||||||
import cn.hutool.core.io.resource.Resource;
|
import cn.hutool.core.io.resource.Resource;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.net.SSLUtil;
|
||||||
import cn.hutool.core.net.url.UrlBuilder;
|
import cn.hutool.core.net.url.UrlBuilder;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
@ -887,11 +888,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
public HttpRequest setSSLProtocol(String protocol) {
|
public HttpRequest setSSLProtocol(String protocol) {
|
||||||
Assert.notBlank(protocol, "protocol must be not blank!");
|
Assert.notBlank(protocol, "protocol must be not blank!");
|
||||||
try {
|
setSSLSocketFactory(SSLUtil.createSSLContext(protocol).getSocketFactory());
|
||||||
setSSLSocketFactory(SSLSocketFactoryBuilder.create().setProtocol(protocol).build());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new HttpException(e);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
package cn.hutool.http.ssl;
|
package cn.hutool.http.ssl;
|
||||||
|
|
||||||
import java.security.KeyManagementException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import cn.hutool.core.net.SSLProtocols;
|
||||||
|
|
||||||
import static cn.hutool.http.ssl.SSLSocketFactoryBuilder.SSLv3;
|
|
||||||
import static cn.hutool.http.ssl.SSLSocketFactoryBuilder.TLSv1;
|
|
||||||
import static cn.hutool.http.ssl.SSLSocketFactoryBuilder.TLSv11;
|
|
||||||
import static cn.hutool.http.ssl.SSLSocketFactoryBuilder.TLSv12;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 兼容android低版本SSL连接<br>
|
* 兼容android低版本SSL连接<br>
|
||||||
@ -20,10 +15,11 @@ import static cn.hutool.http.ssl.SSLSocketFactoryBuilder.TLSv12;
|
|||||||
public class AndroidSupportSSLFactory extends CustomProtocolsSSLFactory {
|
public class AndroidSupportSSLFactory extends CustomProtocolsSSLFactory {
|
||||||
|
|
||||||
// Android低版本不重置的话某些SSL访问就会失败
|
// Android低版本不重置的话某些SSL访问就会失败
|
||||||
private static final String[] protocols = {SSLv3, TLSv1, TLSv11, TLSv12};
|
private static final String[] protocols = {
|
||||||
|
SSLProtocols.SSLv3, SSLProtocols.TLSv1, SSLProtocols.TLSv11, SSLProtocols.TLSv12};
|
||||||
|
|
||||||
public AndroidSupportSSLFactory() throws KeyManagementException, NoSuchAlgorithmException {
|
public AndroidSupportSSLFactory() throws IORuntimeException {
|
||||||
super(protocols);
|
super(protocols);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package cn.hutool.http.ssl;
|
package cn.hutool.http.ssl;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
|
import cn.hutool.core.net.SSLUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
@ -7,8 +9,6 @@ import javax.net.ssl.SSLSocketFactory;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义支持协议类型的SSLSocketFactory
|
* 自定义支持协议类型的SSLSocketFactory
|
||||||
@ -24,12 +24,11 @@ public class CustomProtocolsSSLFactory extends SSLSocketFactory {
|
|||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
* @param protocols 支持协议列表
|
* @param protocols 支持协议列表
|
||||||
* @throws KeyManagementException KeyManagementException
|
* @throws IORuntimeException IO异常
|
||||||
* @throws NoSuchAlgorithmException 无此算法
|
|
||||||
*/
|
*/
|
||||||
public CustomProtocolsSSLFactory(String... protocols) throws KeyManagementException, NoSuchAlgorithmException {
|
public CustomProtocolsSSLFactory(String... protocols) throws IORuntimeException {
|
||||||
this.protocols = protocols;
|
this.protocols = protocols;
|
||||||
this.base = SSLSocketFactoryBuilder.create().build();
|
this.base = SSLUtil.createSSLContext(null).getSocketFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -90,7 +89,7 @@ public class CustomProtocolsSSLFactory extends SSLSocketFactory {
|
|||||||
* @param socket SSLSocket
|
* @param socket SSLSocket
|
||||||
*/
|
*/
|
||||||
private void resetProtocols(SSLSocket socket) {
|
private void resetProtocols(SSLSocket socket) {
|
||||||
if(ArrayUtil.isNotEmpty(this.protocols)){
|
if (ArrayUtil.isNotEmpty(this.protocols)) {
|
||||||
socket.setEnabledProtocols(this.protocols);
|
socket.setEnabledProtocols(this.protocols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package cn.hutool.http.ssl;
|
package cn.hutool.http.ssl;
|
||||||
|
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认的SSLSocketFactory
|
* 默认的SSLSocketFactory
|
||||||
*
|
*
|
||||||
@ -11,7 +8,7 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
*/
|
*/
|
||||||
public class DefaultSSLFactory extends CustomProtocolsSSLFactory {
|
public class DefaultSSLFactory extends CustomProtocolsSSLFactory {
|
||||||
|
|
||||||
public DefaultSSLFactory() throws KeyManagementException, NoSuchAlgorithmException {
|
public DefaultSSLFactory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
package cn.hutool.http.ssl;
|
package cn.hutool.http.ssl;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.http.HttpException;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认的SSL配置,当用户未设置相关信息时,使用默认设置,默认设置为单例模式。
|
* 默认的全局SSL配置,当用户未设置相关信息时,使用默认设置,默认设置为单例模式。
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
* @since 5.1.2
|
* @since 5.1.2
|
||||||
@ -25,16 +22,11 @@ public class DefaultSSLInfo {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
TRUST_ANY_HOSTNAME_VERIFIER = new TrustAnyHostnameVerifier();
|
TRUST_ANY_HOSTNAME_VERIFIER = new TrustAnyHostnameVerifier();
|
||||||
|
if (StrUtil.equalsIgnoreCase("dalvik", System.getProperty("java.vm.name"))) {
|
||||||
try {
|
// 兼容android低版本SSL连接
|
||||||
if (StrUtil.equalsIgnoreCase("dalvik", System.getProperty("java.vm.name"))) {
|
DEFAULT_SSF = new AndroidSupportSSLFactory();
|
||||||
// 兼容android低版本SSL连接
|
} else {
|
||||||
DEFAULT_SSF = new AndroidSupportSSLFactory();
|
DEFAULT_SSF = new DefaultSSLFactory();
|
||||||
} else {
|
|
||||||
DEFAULT_SSF = new DefaultSSLFactory();
|
|
||||||
}
|
|
||||||
} catch (KeyManagementException | NoSuchAlgorithmException e) {
|
|
||||||
throw new HttpException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.hutool.http.ssl;
|
package cn.hutool.http.ssl;
|
||||||
|
|
||||||
import cn.hutool.core.net.SSLContextBuilder;
|
import cn.hutool.core.net.SSLContextBuilder;
|
||||||
|
import cn.hutool.core.net.SSLProtocols;
|
||||||
|
|
||||||
import javax.net.ssl.KeyManager;
|
import javax.net.ssl.KeyManager;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
@ -14,38 +15,10 @@ import java.security.SecureRandom;
|
|||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
* @see SSLContextBuilder
|
* @see SSLContextBuilder
|
||||||
|
* @deprecated 请使用 {@link SSLContextBuilder}
|
||||||
*/
|
*/
|
||||||
public class SSLSocketFactoryBuilder {
|
@Deprecated
|
||||||
|
public class SSLSocketFactoryBuilder implements SSLProtocols {
|
||||||
/**
|
|
||||||
* Supports some version of SSL; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String SSL = SSLContextBuilder.SSL;
|
|
||||||
/**
|
|
||||||
* Supports SSL version 2 or later; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String SSLv2 = SSLContextBuilder.SSLv2;
|
|
||||||
/**
|
|
||||||
* Supports SSL version 3; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String SSLv3 = SSLContextBuilder.SSLv3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supports some version of TLS; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String TLS = SSLContextBuilder.TLS;
|
|
||||||
/**
|
|
||||||
* Supports RFC 2246: TLS version 1.0 ; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String TLSv1 = SSLContextBuilder.TLSv1;
|
|
||||||
/**
|
|
||||||
* Supports RFC 4346: TLS version 1.1 ; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String TLSv11 = SSLContextBuilder.TLSv11;
|
|
||||||
/**
|
|
||||||
* Supports RFC 5246: TLS version 1.2 ; may support other versions
|
|
||||||
*/
|
|
||||||
public static final String TLSv12 = SSLContextBuilder.TLSv12;
|
|
||||||
|
|
||||||
SSLContextBuilder sslContextBuilder;
|
SSLContextBuilder sslContextBuilder;
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ package cn.hutool.http;
|
|||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.date.TimeInterval;
|
import cn.hutool.core.date.TimeInterval;
|
||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
|
import cn.hutool.core.net.SSLProtocols;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.http.ssl.SSLSocketFactoryBuilder;
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ public class HttpRequestTest {
|
|||||||
// 禁用缓存
|
// 禁用缓存
|
||||||
.disableCache()
|
.disableCache()
|
||||||
// 自定义SSL版本
|
// 自定义SSL版本
|
||||||
.setSSLProtocol(SSLSocketFactoryBuilder.TLSv12);
|
.setSSLProtocol(SSLProtocols.TLSv12);
|
||||||
Console.log(request.execute().body());
|
Console.log(request.execute().body());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user