add HttpDownloader

This commit is contained in:
Looly 2021-04-25 18:13:44 +08:00
parent 2cedffa492
commit b14bbffa32
7 changed files with 157 additions and 83 deletions

View File

@ -3,7 +3,7 @@
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.6.4 (2021-04-23) # 5.6.4 (2021-04-25)
### 🐣新特性 ### 🐣新特性
* 【core 】 DatePattern补充DateTimeFormatterpr#308@Gitee * 【core 】 DatePattern补充DateTimeFormatterpr#308@Gitee
@ -13,6 +13,7 @@
* 【core 】 BeanUtil增加copyToList方法issue#1526@Github * 【core 】 BeanUtil增加copyToList方法issue#1526@Github
* 【extra 】 MailAccount增加customProperty可以用户自定义属性pr#317@Gitee * 【extra 】 MailAccount增加customProperty可以用户自定义属性pr#317@Gitee
* 【system 】 SystemUtil.getUserInfo()中所有平台路径统一末尾加/issue#I3NM39@Gitee * 【system 】 SystemUtil.getUserInfo()中所有平台路径统一末尾加/issue#I3NM39@Gitee
* 【http 】 新增HttpDownloader默认开启自动跳转issue#I3NM39@Gitee
### 🐞Bug修复 ### 🐞Bug修复
* 【db 】 修复SQL分页时未使用别名导致的错误同时count时取消order by子句issue#I3IJ8X@Gitee * 【db 】 修复SQL分页时未使用别名导致的错误同时count时取消order by子句issue#I3IJ8X@Gitee

View File

@ -130,8 +130,7 @@ public class DesensitizedUtil {
if (StrUtil.isBlank(fullName)) { if (StrUtil.isBlank(fullName)) {
return StrUtil.EMPTY; return StrUtil.EMPTY;
} }
final String name = StrUtil.subPre(fullName, 1); return StrUtil.hide(fullName, 1, fullName.length());
return StrUtil.padAfter(name, StrUtil.length(fullName), "*");
} }
/** /**

View File

@ -1,6 +1,8 @@
package cn.hutool.core.util; package cn.hutool.core.util;
/** /**
* 进制转换工具类可以转换为任意进制
* <p>
* 把一个十进制整数根据自己定义的进制规则进行转换<br> * 把一个十进制整数根据自己定义的进制规则进行转换<br>
* fromhttps://gitee.com/loolly/hutool/pulls/260 * fromhttps://gitee.com/loolly/hutool/pulls/260
* <p> * <p>

View File

@ -23,7 +23,7 @@ import java.util.ServiceLoader;
public class ServiceLoaderUtil { public class ServiceLoaderUtil {
/** /**
* 加载第一个可用服务如果用户定义了多个接口实现类只获取第一个不报错的服务 * 加载第一个可用服务如果用户定义了多个接口实现类只获取第一个不报错的服务
* *
* @param <T> 接口类型 * @param <T> 接口类型
* @param clazz 服务接口 * @param clazz 服务接口

View File

@ -0,0 +1,105 @@
package cn.hutool.http;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.lang.Assert;
import java.io.File;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* 下载封装下载统一使用{@code GET}请求默认支持30x跳转
*
* @since 5.6.4
* @author looly
*/
public class HttpDownloader {
/**
* 下载远程文本
*
* @param url 请求的url
* @param customCharset 自定义的字符集可以使用{@code CharsetUtil#charset} 方法转换
* @param streamPress 进度条 {@link StreamProgress}
* @return 文本
*/
public static String downloadString(String url, Charset customCharset, StreamProgress streamPress) {
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
download(url, out, true, streamPress);
return null == customCharset ? out.toString() : out.toString(customCharset);
}
/**
* 下载远程文件数据支持30x跳转
*
* @param url 请求的url
* @return 文件数据
*/
public static byte[] downloadBytes(String url) {
return requestDownload(url, -1).bodyBytes();
}
/**
* 下载远程文件
*
* @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param timeout 超时单位毫秒-1表示默认超时
* @param streamProgress 进度条
* @return 文件大小
*/
public static long downloadFile(String url, File destFile, int timeout, StreamProgress streamProgress) {
return requestDownload(url, timeout).writeBody(destFile, streamProgress);
}
/**
* 下载远程文件返回文件
*
* @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param timeout 超时单位毫秒-1表示默认超时
* @param streamProgress 进度条
* @return 文件
*/
public static File downloadForFile(String url, File destFile, int timeout, StreamProgress streamProgress) {
return requestDownload(url, timeout).writeBodyForFile(destFile, streamProgress);
}
/**
* 下载远程文件
*
* @param url 请求的url
* @param out 将下载内容写到输出流中 {@link OutputStream}
* @param isCloseOut 是否关闭输出流
* @param streamProgress 进度条
* @return 文件大小
*/
public static long download(String url, OutputStream out, boolean isCloseOut, StreamProgress streamProgress) {
Assert.notNull(out, "[out] is null !");
return requestDownload(url, -1).writeBody(out, isCloseOut, streamProgress);
}
/**
* 请求下载文件
*
* @param url 请求下载文件地址
* @param timeout 超时时间
* @return HttpResponse
* @since 5.4.1
*/
private static HttpResponse requestDownload(String url, int timeout) {
Assert.notBlank(url, "[url] is blank !");
final HttpResponse response = HttpUtil.createGet(url, true)
.timeout(timeout)
.executeAsync();
if (response.isOk()) {
return response;
}
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
}
}

View File

@ -278,10 +278,27 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
Assert.notNull(destFile, "[destFile] is null!"); Assert.notNull(destFile, "[destFile] is null!");
final File outFile = completeFileNameFromHeader(destFile); final File outFile = completeFileNameFromHeader(destFile);
final OutputStream outputStream = FileUtil.getOutputStream(outFile); return writeBody(FileUtil.getOutputStream(outFile), true, streamProgress);
return writeBody(outputStream, true, streamProgress);
} }
/**
* 将响应内容写出到文件<br>
* 异步模式下直接读取Http流写出同步模式下将存储在内存中的响应内容写出<br>
* 写出后会关闭Http流异步模式
*
* @param destFile 写出到的文件
* @param streamProgress 进度显示接口通过实现此接口显示下载进度
* @return 写出的文件
* @since 5.6.4
*/
public File writeBodyForFile(File destFile, StreamProgress streamProgress) {
Assert.notNull(destFile, "[destFile] is null!");
final File outFile = completeFileNameFromHeader(destFile);
writeBody(FileUtil.getOutputStream(outFile), true, streamProgress);
return outFile;
}
/** /**
* 将响应内容写出到文件<br> * 将响应内容写出到文件<br>

View File

@ -2,11 +2,9 @@ package cn.hutool.http;
import cn.hutool.core.codec.Base64; import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.StreamProgress; import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlQuery; import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.text.StrBuilder; import cn.hutool.core.text.StrBuilder;
@ -85,7 +83,19 @@ public class HttpUtil {
* @since 3.2.0 * @since 3.2.0
*/ */
public static HttpRequest createGet(String url) { public static HttpRequest createGet(String url) {
return HttpRequest.get(url); return createGet(url, false);
}
/**
* 创建Http GET请求对象
*
* @param url 请求的URL可以使HTTP或者HTTPS
* @param isFollowRedirects 是否打开重定向
* @return {@link HttpRequest}
* @since 5.6.4
*/
public static HttpRequest createGet(String url, boolean isFollowRedirects) {
return HttpRequest.get(url).setFollowRedirects(isFollowRedirects);
} }
/** /**
@ -249,13 +259,7 @@ public class HttpUtil {
* @return 文本 * @return 文本
*/ */
public static String downloadString(String url, Charset customCharset, StreamProgress streamPress) { public static String downloadString(String url, Charset customCharset, StreamProgress streamPress) {
if (StrUtil.isBlank(url)) { return HttpDownloader.downloadString(url, customCharset, streamPress);
throw new NullPointerException("[url] is null!");
}
FastByteArrayOutputStream out = new FastByteArrayOutputStream();
download(url, out, true, streamPress);
return null == customCharset ? out.toString() : out.toString(customCharset);
} }
/** /**
@ -316,7 +320,7 @@ public class HttpUtil {
* @since 4.0.4 * @since 4.0.4
*/ */
public static long downloadFile(String url, File destFile, int timeout, StreamProgress streamProgress) { public static long downloadFile(String url, File destFile, int timeout, StreamProgress streamProgress) {
return requestDownloadFile(url, destFile, timeout).writeBody(destFile, streamProgress); return HttpDownloader.downloadFile(url, destFile, timeout, streamProgress);
} }
/** /**
@ -324,7 +328,6 @@ public class HttpUtil {
* *
* @param url 请求的url * @param url 请求的url
* @param dest 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名 * @param dest 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
*
* @return 下载的文件对象 * @return 下载的文件对象
* @since 5.4.1 * @since 5.4.1
*/ */
@ -337,7 +340,6 @@ public class HttpUtil {
* *
* @param url 请求的url * @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名 * @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
*
* @return 下载的文件对象 * @return 下载的文件对象
* @since 5.4.1 * @since 5.4.1
*/ */
@ -351,7 +353,6 @@ public class HttpUtil {
* @param url 请求的url * @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名 * @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param timeout 超时单位毫秒-1表示默认超时 * @param timeout 超时单位毫秒-1表示默认超时
*
* @return 下载的文件对象 * @return 下载的文件对象
* @since 5.4.1 * @since 5.4.1
*/ */
@ -365,7 +366,6 @@ public class HttpUtil {
* @param url 请求的url * @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名 * @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param streamProgress 进度条 * @param streamProgress 进度条
*
* @return 下载的文件对象 * @return 下载的文件对象
* @since 5.4.1 * @since 5.4.1
*/ */
@ -380,38 +380,11 @@ public class HttpUtil {
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名 * @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param timeout 超时单位毫秒-1表示默认超时 * @param timeout 超时单位毫秒-1表示默认超时
* @param streamProgress 进度条 * @param streamProgress 进度条
*
* @return 下载的文件对象 * @return 下载的文件对象
* @since 5.4.1 * @since 5.4.1
*/ */
public static File downloadFileFromUrl(String url, File destFile, int timeout, StreamProgress streamProgress) { public static File downloadFileFromUrl(String url, File destFile, int timeout, StreamProgress streamProgress) {
HttpResponse response = requestDownloadFile(url, destFile, timeout); return HttpDownloader.downloadForFile(url, destFile, timeout, streamProgress);
final File outFile = response.completeFileNameFromHeader(destFile);
long writeBytes = response.writeBody(outFile, streamProgress);
return outFile;
}
/**
* 请求下载文件
*
* @param url 请求下载文件地址
* @param destFile 目标目录或者目标文件
* @param timeout 超时时间
*
* @return HttpResponse
* @since 5.4.1
*/
private static HttpResponse requestDownloadFile(String url, File destFile, int timeout) {
Assert.notBlank(url, "[url] is blank !");
Assert.notNull(destFile, "[destFile] is null !");
final HttpResponse response = HttpRequest.get(url).timeout(timeout).executeAsync();
if (response.isOk()) {
return response;
}
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
} }
/** /**
@ -420,7 +393,6 @@ public class HttpUtil {
* @param url 请求的url * @param url 请求的url
* @param out 将下载内容写到输出流中 {@link OutputStream} * @param out 将下载内容写到输出流中 {@link OutputStream}
* @param isCloseOut 是否关闭输出流 * @param isCloseOut 是否关闭输出流
*
* @return 文件大小 * @return 文件大小
*/ */
public static long download(String url, OutputStream out, boolean isCloseOut) { public static long download(String url, OutputStream out, boolean isCloseOut) {
@ -437,40 +409,18 @@ public class HttpUtil {
* @return 文件大小 * @return 文件大小
*/ */
public static long download(String url, OutputStream out, boolean isCloseOut, StreamProgress streamProgress) { public static long download(String url, OutputStream out, boolean isCloseOut, StreamProgress streamProgress) {
if (StrUtil.isBlank(url)) { return HttpDownloader.download(url, out, isCloseOut, streamProgress);
throw new NullPointerException("[url] is null!");
}
if (null == out) {
throw new NullPointerException("[out] is null!");
}
final HttpResponse response = HttpRequest.get(url).executeAsync();
if (!response.isOk()) {
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
}
return response.writeBody(out, isCloseOut, streamProgress);
} }
/** /**
* 下载远程文件数据支持30x跳转 * 下载远程文件数据支持30x跳转
* *
* @param url 请求的url * @param url 请求的url
*
* @return 文件数据 * @return 文件数据
*
* @since 5.3.6 * @since 5.3.6
*/ */
public static byte[] downloadBytes(String url) { public static byte[] downloadBytes(String url) {
if (StrUtil.isBlank(url)) { return HttpDownloader.downloadBytes(url);
throw new NullPointerException("[url] is null!");
}
final HttpResponse response = HttpRequest.get(url)
.setFollowRedirects(true).executeAsync();
if (!response.isOk()) {
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
}
return response.bodyBytes();
} }
/** /**
@ -884,7 +834,7 @@ public class HttpUtil {
* @return 密码验证信息 * @return 密码验证信息
* @since 5.4.6 * @since 5.4.6
*/ */
public static String buildBasicAuth(String username, String password, Charset charset){ public static String buildBasicAuth(String username, String password, Charset charset) {
final String data = username.concat(":").concat(password); final String data = username.concat(":").concat(password);
return "Basic " + Base64.encode(data, charset); return "Basic " + Base64.encode(data, charset);
} }