diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/ClientConfig.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/ClientConfig.java
index b6c580fee..c96a1d1f8 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/ClientConfig.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/ClientConfig.java
@@ -56,10 +56,6 @@ public class ClientConfig {
* 代理
*/
private ProxyInfo proxy;
- /**
- * 是否遇到响应状态码3xx时自动重定向请求
- */
- private boolean followRedirects;
/**
* 是否使用引擎默认的Cookie管理器,默认为true
* 默认情况下每个客户端维护一个自己的Cookie管理器,这个管理器用于在多次请求中记录并自动附带Cookie信息
@@ -215,28 +211,6 @@ public class ClientConfig {
return this;
}
- /**
- * 是否遇到响应状态码3xx时自动重定向请求
- * 注意:当打开客户端级别的自动重定向,则{@link Request#maxRedirects()}无效
- *
- * @return 是否遇到响应状态码3xx时自动重定向请求
- */
- public boolean isFollowRedirects() {
- return followRedirects;
- }
-
- /**
- * 设置是否遇到响应状态码3xx时自动重定向请求
- * 注意:当打开客户端级别的自动重定向,则{@link Request#maxRedirects()}无效
- *
- * @param followRedirects 是否遇到响应状态码3xx时自动重定向请求
- * @return this
- */
- public ClientConfig setFollowRedirects(final boolean followRedirects) {
- this.followRedirects = followRedirects;
- return this;
- }
-
/**
* 是否使用引擎默认的Cookie管理器,默认为true
* 默认情况下每个客户端维护一个自己的Cookie管理器,这个管理器用于在多次请求中记录并自动附带Cookie信息
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/HttpDownloader.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/HttpDownloader.java
index ac2baad05..02bb69964 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/HttpDownloader.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/HttpDownloader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2024 Hutool Team and hutool.cn
+ * Copyright (c) 2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,155 +16,189 @@
package org.dromara.hutool.http.client;
+import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.StreamProgress;
-import org.dromara.hutool.core.io.stream.FastByteArrayOutputStream;
-import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.http.HttpException;
+import org.dromara.hutool.http.client.body.ResponseBody;
+import org.dromara.hutool.http.client.engine.ClientEngine;
import org.dromara.hutool.http.client.engine.ClientEngineFactory;
import java.io.File;
+import java.io.IOException;
import java.io.OutputStream;
-import java.nio.charset.Charset;
+import java.util.Map;
/**
- * 下载封装,下载统一使用{@code GET}请求,默认支持30x跳转
+ * HTTP下载器,两种使用方式:
+ * 1. 一次性使用:
+ *
{@code HttpDownloader.of(url).downloadFile(file)}
+ * 2. 多次下载复用:
+ * {@code
+ * HttpDownloader downloader = HttpDownloader.of(url).setCloseEngine(false);
+ * downloader.downloadFile(file);
+ * downloader.downloadFile(file2);
+ * downloader.close();
+ * }
*
* @author looly
- * @since 5.6.4
+ * @since 6.0.0
*/
-@SuppressWarnings("resource")
public class HttpDownloader {
/**
- * 下载远程文本
+ * 创建下载器
*
- * @param url 请求的url
- * @param customCharset 自定义的字符集,可以使用{@code CharsetUtil#charset} 方法转换
- * @param streamPress 进度条 {@link StreamProgress}
- * @return 文本
+ * @param url 请求地址
+ * @return 下载器
*/
- public static String downloadString(final String url, final Charset customCharset, final StreamProgress streamPress) {
- final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
- download(url, out, true, streamPress);
- return null == customCharset ? out.toString() : out.toString(customCharset);
+ public static HttpDownloader of(final String url) {
+ return new HttpDownloader(url);
+ }
+
+ private final Request request;
+ private ClientConfig config;
+ private ClientEngine engine;
+ private StreamProgress streamProgress;
+ private boolean closeEngine = true;
+
+ /**
+ * 构造
+ *
+ * @param url 请求地址
+ */
+ public HttpDownloader(final String url) {
+ this.request = Request.of(url);
}
/**
- * 下载远程文件数据,支持30x跳转
+ * 设置请求头
*
- * @param url 请求的url
- * @return 文件数据
+ * @param headers 请求头
+ * @return this
*/
- public static byte[] downloadBytes(final String url) {
- return downloadBytes(url, 0);
+ public HttpDownloader header(final Map headers) {
+ this.request.header(headers);
+ return this;
}
/**
- * 下载远程文件数据,支持30x跳转
+ * 设置配置
*
- * @param url 请求的url
- * @param timeout 超时毫秒数
- * @return 文件数据
- * @since 5.8.28
+ * @param config 配置
+ * @return this
*/
- public static byte[] downloadBytes(final String url, final int timeout) {
- return requestDownload(url, timeout).bodyBytes();
+ public HttpDownloader setConfig(final ClientConfig config) {
+ this.config = config;
+ return this;
}
/**
- * 下载远程文件
+ * 设置超时
*
- * @param url 请求的url
- * @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @return 文件
+ * @param milliseconds 超时毫秒数
+ * @return this
*/
- public static File downloadFile(final String url, final File targetFileOrDir) {
- return downloadFile(url, targetFileOrDir, -1);
+ public HttpDownloader setTimeout(final int milliseconds) {
+ if (null == this.config) {
+ this.config = ClientConfig.of();
+ }
+ this.config.setTimeout(milliseconds);
+ return this;
}
/**
- * 下载远程文件
+ * 设置引擎,用于自定义引擎
*
- * @param url 请求的url
- * @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param timeout 超时,单位毫秒,-1表示默认超时
- * @return 文件
+ * @param engine 引擎
+ * @return this
*/
- public static File downloadFile(final String url, final File targetFileOrDir, final int timeout) {
- Assert.notNull(targetFileOrDir, "[targetFileOrDir] is null !");
- return downloadFile(url, targetFileOrDir, timeout, null);
+ public HttpDownloader setEngine(final ClientEngine engine) {
+ this.engine = engine;
+ return this;
}
/**
- * 下载远程文件
+ * 设置进度条
*
- * @param url 请求的url
- * @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param timeout 超时,单位毫秒,-1表示默认超时
- * @param streamProgress 进度条
- * @return 文件
- */
- public static File downloadFile(final String url, final File targetFileOrDir, final int timeout, final StreamProgress streamProgress) {
- Assert.notNull(targetFileOrDir, "[targetFileOrDir] is null !");
- return requestDownload(url, timeout).body().write(targetFileOrDir, streamProgress);
- }
-
- /**
- * 下载文件-避免未完成的文件
- * 来自:https://gitee.com/dromara/hutool/pulls/407
- * 此方法原理是先在目标文件同级目录下创建临时文件,下载之,等下载完毕后重命名,避免因下载错误导致的文件不完整。
- *
- * @param url 请求的url
- * @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param tempFileSuffix 临时文件后缀,默认".temp"
- * @param timeout 超时,单位毫秒,-1表示默认超时
- * @param streamProgress 进度条
- * @return 文件
- * @since 5.7.12
- */
- public static File downloadFile(final String url, final File targetFileOrDir, final String tempFileSuffix, final int timeout, final StreamProgress streamProgress) {
- Assert.notNull(targetFileOrDir, "[targetFileOrDir] is null !");
- return requestDownload(url, timeout).body().write(targetFileOrDir, tempFileSuffix, streamProgress);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param out 将下载内容写到输出流中 {@link OutputStream}
- * @param isCloseOut 是否关闭输出流
* @param streamProgress 进度条
- * @return 文件大小
+ * @return this
*/
- public static long download(final String url, final OutputStream out, final boolean isCloseOut, final StreamProgress streamProgress) {
- Assert.notNull(out, "[out] is null !");
- return requestDownload(url, -1).body().write(out, isCloseOut, streamProgress);
+ public HttpDownloader setStreamProgress(final StreamProgress streamProgress) {
+ this.streamProgress = streamProgress;
+ return this;
}
/**
- * 请求下载文件
+ * 设置是否关闭引擎,默认为true,即自动关闭引擎
*
- * @param url 请求下载文件地址
- * @param timeout 超时时间
- * @return HttpResponse
- * @since 5.4.1
+ * @param closeEngine 是否关闭引擎
+ * @return this
*/
- private static Response requestDownload(final String url, final int timeout) {
- Assert.notBlank(url, "[url] is blank !");
+ public HttpDownloader setCloseEngine(final boolean closeEngine) {
+ this.closeEngine = closeEngine;
+ return this;
+ }
- final ClientConfig config = ClientConfig.of();
- if(timeout > 0){
- config.setTimeout(timeout);
+ /**
+ * 下载文件
+ *
+ * @param targetFileOrDir 目标文件或目录,当为目录时,自动使用文件名作为下载文件名
+ * @return 下载文件
+ */
+ public File downloadFile(final File targetFileOrDir) {
+ return downloadFile(targetFileOrDir, null);
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param targetFileOrDir 目标文件或目录,当为目录时,自动使用文件名作为下载文件名
+ * @param tempFileSuffix 临时文件后缀
+ * @return 下载文件
+ */
+ public File downloadFile(final File targetFileOrDir, final String tempFileSuffix) {
+ try (final ResponseBody body = send()) {
+ return body.write(targetFileOrDir, tempFileSuffix, this.streamProgress);
+ } catch (final IOException e) {
+ throw new HttpException(e);
+ } finally {
+ if (this.closeEngine) {
+ IoUtil.closeQuietly(this.engine);
+ }
}
+ }
- final Response response = ClientEngineFactory.getEngine()
- .init(config)
- .send(Request.of(url));
-
- if (response.isOk()) {
- return response;
+ /**
+ * 下载文件
+ *
+ * @param out 输出流
+ * @param isCloseOut 是否关闭输出流,true关闭
+ * @return 下载的字节数
+ */
+ public long download(final OutputStream out, final boolean isCloseOut) {
+ try (final ResponseBody body = send()) {
+ return body.write(out, isCloseOut, this.streamProgress);
+ } catch (final IOException e) {
+ throw new HttpException(e);
+ } finally {
+ if (this.closeEngine) {
+ IoUtil.closeQuietly(this.engine);
+ }
}
+ }
- throw new HttpException("Server response error with status code: [{}]", response.getStatus());
+ /**
+ * 发送请求,获取响应
+ *
+ * @return 响应
+ */
+ private ResponseBody send() {
+ if (null == this.engine) {
+ this.engine = ClientEngineFactory.createEngine();
+ }
+ if (null != this.config) {
+ this.engine.init(this.config);
+ }
+ return engine.send(this.request).body();
}
}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/Request.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/Request.java
index 0270d0022..30aa405c4 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/Request.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/Request.java
@@ -138,6 +138,11 @@ public class Request implements HeaderOperation {
* 是否是REST请求模式,REST模式运行GET请求附带body
*/
private boolean isRest;
+ /**
+ * 重定向计数器,内部使用
+ * 单次请求时,重定向次数内部自增
+ */
+ private int redirectCount;
/**
* 默认构造
@@ -399,7 +404,7 @@ public class Request implements HeaderOperation {
/**
* 获取最大重定向请求次数
- * 注意:当{@link ClientConfig#isFollowRedirects()}为{@code true}时,此参数无效
+ * 如果次数小于1则表示不重定向,大于等于1表示打开重定向。
*
* @return 最大重定向请求次数
*/
@@ -410,7 +415,6 @@ public class Request implements HeaderOperation {
/**
* 设置最大重定向次数
* 如果次数小于1则表示不重定向,大于等于1表示打开重定向
- * 注意:当{@link ClientConfig#isFollowRedirects()}为{@code true}时,此参数无效
*
* @param maxRedirects 最大重定向次数
* @return this
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/RequestContext.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/RequestContext.java
new file mode 100644
index 000000000..ad7b8bc8f
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/RequestContext.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.http.client;
+
+/**
+ * 请求上下文,用于在多次请求时保存状态信息
+ * 非线程安全。
+ *
+ * @author Looly
+ */
+public class RequestContext {
+
+ private Request request;
+ private int redirectCount;
+
+ /**
+ * 获取请求
+ *
+ * @return 请求
+ */
+ public Request getRequest() {
+ return request;
+ }
+
+ /**
+ * 设置请求
+ *
+ * @param request 请求
+ * @return this
+ */
+ public RequestContext setRequest(final Request request) {
+ this.request = request;
+ return this;
+ }
+
+ /**
+ * 是否允许重定向,如果允许,则重定向计数器+1
+ *
+ * @return 是否允许重定向
+ */
+ public boolean canRedirect(){
+ final int maxRedirects = request.maxRedirects();
+ if(maxRedirects > 0 && redirectCount < maxRedirects){
+ redirectCount++;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Engine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
index 1a7c8e3cc..aa74f486d 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
@@ -134,13 +134,6 @@ public class HttpClient4Engine extends AbstractClientEngine {
// 设置默认头信息
clientBuilder.setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()));
- // 重定向
- if (config.isFollowRedirects()) {
- clientBuilder.setRedirectStrategy(LaxRedirectStrategy.INSTANCE);
- } else {
- clientBuilder.disableRedirectHandling();
- }
-
// 设置代理
setProxy(clientBuilder, config);
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Engine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
index 16d092bd7..f0e4ac950 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
@@ -21,7 +21,6 @@ import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.impl.DefaultRedirectStrategy;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
@@ -122,7 +121,7 @@ public class HttpClient5Engine extends AbstractClientEngine {
clientBuilder.setConnectionManager(buildConnectionManager(config));
// 实例级别默认请求配置
- clientBuilder.setDefaultRequestConfig(buildRequestConfig(config));
+ clientBuilder.setDefaultRequestConfig(buildDefaultRequestConfig(config));
// 缓存
if (config.isDisableCache()) {
@@ -132,13 +131,6 @@ public class HttpClient5Engine extends AbstractClientEngine {
// 设置默认头信息
clientBuilder.setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()));
- // 重定向
- if (config.isFollowRedirects()) {
- clientBuilder.setRedirectStrategy(DefaultRedirectStrategy.INSTANCE);
- } else {
- clientBuilder.disableRedirectHandling();
- }
-
// 设置代理
setProxy(clientBuilder, config);
@@ -261,12 +253,12 @@ public class HttpClient5Engine extends AbstractClientEngine {
}
/**
- * 构建请求配置,包括连接请求超时和响应(读取)超时
+ * 构建默认请求配置,包括连接请求超时和响应(读取)超时
*
* @param config {@link ClientConfig}
* @return {@link RequestConfig}
*/
- private static RequestConfig buildRequestConfig(final ClientConfig config) {
+ private static RequestConfig buildDefaultRequestConfig(final ClientConfig config) {
final int connectionTimeout = config.getConnectionTimeout();
// 请求配置
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkClientEngine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkClientEngine.java
index 1e9ab1169..95eb24277 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkClientEngine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkClientEngine.java
@@ -27,6 +27,7 @@ import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.HttpUtil;
import org.dromara.hutool.http.client.ClientConfig;
import org.dromara.hutool.http.client.Request;
+import org.dromara.hutool.http.client.RequestContext;
import org.dromara.hutool.http.client.body.HttpBody;
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
import org.dromara.hutool.http.meta.HeaderName;
@@ -69,16 +70,7 @@ public class JdkClientEngine extends AbstractClientEngine {
@Override
public JdkHttpResponse send(final Request message) {
- final JdkHttpConnection conn = buildConn(message);
- try {
- doSend(conn, message);
- } catch (final IOException e) {
- // 出错后关闭连接
- IoUtil.closeQuietly(conn);
- throw new IORuntimeException(e);
- }
-
- return sendRedirectIfPossible(conn, message);
+ return doSend(new RequestContext().setRequest(message));
}
@Override
@@ -102,6 +94,20 @@ public class JdkClientEngine extends AbstractClientEngine {
this.cookieManager = (null != this.config && this.config.isUseCookieManager()) ? new JdkCookieManager() : new JdkCookieManager(null);
}
+ private JdkHttpResponse doSend(final RequestContext context) {
+ final Request message = context.getRequest();
+ final JdkHttpConnection conn = buildConn(message);
+ try {
+ doSend(conn, message);
+ } catch (final IOException e) {
+ // 出错后关闭连接
+ IoUtil.closeQuietly(conn);
+ throw new IORuntimeException(e);
+ }
+
+ return sendRedirectIfPossible(conn, context);
+ }
+
/**
* 执行发送
*
@@ -132,7 +138,7 @@ public class JdkClientEngine extends AbstractClientEngine {
final URL url = message.handledUrl().toURL();
Proxy proxy = null;
final ProxyInfo proxyInfo = config.getProxy();
- if(null != proxyInfo){
+ if (null != proxyInfo) {
proxy = proxyInfo.selectFirst(UrlUtil.toURI(url));
}
final JdkHttpConnection conn = JdkHttpConnection
@@ -141,8 +147,8 @@ public class JdkClientEngine extends AbstractClientEngine {
.setReadTimeout(config.getReadTimeout())
.setMethod(message.method())//
.setSSLInfo(config.getSslInfo())
- // 如果客户端设置自动重定向,则Request中maxRedirectCount无效
- .setInstanceFollowRedirects(config.isFollowRedirects())
+ // 关闭自动重定向,手动处理重定向
+ .setInstanceFollowRedirects(false)
.setDisableCache(config.isDisableCache())
// 覆盖默认Header
.header(message.headers(), true);
@@ -171,10 +177,12 @@ public class JdkClientEngine extends AbstractClientEngine {
/**
* 调用转发,如果需要转发返回转发结果,否则返回{@code null}
*
- * @param conn {@link JdkHttpConnection}}
+ * @param conn {@link JdkHttpConnection}}
+ * @param context 请求上下文
* @return {@link JdkHttpResponse},无转发返回 {@code null}
*/
- private JdkHttpResponse sendRedirectIfPossible(final JdkHttpConnection conn, final Request message) {
+ private JdkHttpResponse sendRedirectIfPossible(final JdkHttpConnection conn, final RequestContext context) {
+ final Request message = context.getRequest();
// 手动实现重定向
if (message.maxRedirects() > 0) {
final int code;
@@ -198,16 +206,15 @@ public class JdkClientEngine extends AbstractClientEngine {
message.method(Method.GET);
}
- if (conn.redirectCount < message.maxRedirects()) {
- conn.redirectCount++;
- return send(message);
+ if (context.canRedirect()) {
+ return doSend(context);
}
}
}
}
// 最终页面
- return new JdkHttpResponse(conn, this.cookieManager, message);
+ return new JdkHttpResponse(conn, this.cookieManager, context.getRequest());
}
/**
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpConnection.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpConnection.java
index 57fd62d59..3e0588561 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpConnection.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpConnection.java
@@ -47,10 +47,6 @@ public class JdkHttpConnection implements HeaderOperation, Cl
private final URL url;
private final Proxy proxy;
private final HttpURLConnection conn;
- /**
- * 重定向次数计数器,内部使用
- */
- protected int redirectCount;
/**
* 创建HttpConnection
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpEngine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpEngine.java
index e8a38ff7d..baca6a38f 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpEngine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpEngine.java
@@ -130,8 +130,8 @@ public class OkHttpEngine extends AbstractClientEngine {
builder.connectionPool(((OkHttpClientConfig) config).getConnectionPool());
}
- // 重定向
- builder.followRedirects(config.isFollowRedirects());
+ // 关闭自动重定向,手动实现
+ builder.followRedirects(false);
// 设置代理
setProxy(builder, config);
@@ -168,7 +168,6 @@ public class OkHttpEngine extends AbstractClientEngine {
// 填充头信息
message.headers().forEach((key, values) -> values.forEach(value -> builder.addHeader(key, value)));
-
return builder.build();
}
diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/client/DownloadTest.java b/hutool-http/src/test/java/org/dromara/hutool/http/client/DownloadTest.java
index f07cca45e..85268d6e3 100644
--- a/hutool-http/src/test/java/org/dromara/hutool/http/client/DownloadTest.java
+++ b/hutool-http/src/test/java/org/dromara/hutool/http/client/DownloadTest.java
@@ -16,23 +16,17 @@
package org.dromara.hutool.http.client;
-import org.dromara.hutool.core.codec.binary.Base64;
-import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.StreamProgress;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.lang.Console;
-import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.http.HttpGlobalConfig;
import org.dromara.hutool.http.client.engine.ClientEngineFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.UUID;
/**
@@ -46,31 +40,22 @@ public class DownloadTest {
@Disabled
public void downloadPicTest() {
final String url = "http://wx.qlogo.cn/mmopen/vKhlFcibVUtNBVDjcIowlg0X8aJfHXrTNCEFBukWVH9ta99pfEN88lU39MKspCUCOP3yrFBH3y2NbV7sYtIIlon8XxLwAEqv2/0";
- HttpDownloader.downloadFile(url, new File("e:/shape/t3.jpg"));
+ HttpDownloader.of(url).downloadFile(new File("d:/test/download/t3.jpg"));
Console.log("ok");
}
- @SuppressWarnings("resource")
- @Test
- @Disabled
- public void downloadSizeTest() {
- final String url = "https://res.t-io.org/im/upload/img/67/8948/1119501/88097554/74541310922/85/231910/366466 - 副本.jpg";
- ClientEngineFactory.getEngine().send(Request.of(url)).body().write("e:/shape/366466.jpg");
- //HttpRequest.get(url).setSSLProtocol("TLSv1.2").executeAsync().body().write("e:/shape/366466.jpg");
- }
-
@Test
@Disabled
public void downloadTest1() {
- final File size = HttpDownloader.downloadFile("http://explorer.bbfriend.com/crossdomain.xml", new File("e:/temp/"));
- System.out.println("Download size: " + size);
+ final File size = HttpDownloader.of("http://explorer.bbfriend.com/crossdomain.xml").downloadFile(new File("d:test/download/temp/"));
+ Console.log("Download size: " + size);
}
@Test
@Disabled
public void downloadTest() {
// 带进度显示的文件下载
- HttpDownloader.downloadFile("http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso", FileUtil.file("d:/"), -1, new StreamProgress() {
+ HttpDownloader.of("http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso").setStreamProgress(new StreamProgress() {
final long time = System.currentTimeMillis();
@@ -89,13 +74,14 @@ public class DownloadTest {
public void finish() {
Console.log("下载完成!");
}
- });
+ }).downloadFile(FileUtil.file("d:/test/"));
}
@Test
@Disabled
public void downloadFileFromUrlTest1() {
- final File file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", new File("d:/download/temp"));
+ final File file = HttpDownloader.of("http://groovy-lang.org/changelogs/changelog-3.0.5.html")
+ .downloadFile(new File("d:/download/temp"));
Assertions.assertNotNull(file);
Assertions.assertTrue(file.isFile());
Assertions.assertTrue(file.length() > 0);
@@ -106,7 +92,8 @@ public class DownloadTest {
public void downloadFileFromUrlTest2() {
File file = null;
try {
- file = HttpDownloader.downloadFile("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), 1, new StreamProgress() {
+ file = HttpDownloader.of("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar")
+ .setStreamProgress(new StreamProgress() {
@Override
public void start() {
System.out.println("start");
@@ -121,15 +108,13 @@ public class DownloadTest {
public void finish() {
System.out.println("end");
}
- });
+ }).downloadFile(FileUtil.file("d:/download/temp"));
Assertions.assertNotNull(file);
Assertions.assertTrue(file.exists());
Assertions.assertTrue(file.isFile());
Assertions.assertTrue(file.length() > 0);
Assertions.assertFalse(file.getName().isEmpty());
- } catch (final Exception e) {
- Assertions.assertInstanceOf(IORuntimeException.class, e);
} finally {
FileUtil.del(file);
}
@@ -140,7 +125,7 @@ public class DownloadTest {
public void downloadFileFromUrlTest3() {
File file = null;
try {
- file = HttpDownloader.downloadFile("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), -1, new StreamProgress() {
+ file = HttpDownloader.of("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar").setStreamProgress(new StreamProgress() {
@Override
public void start() {
System.out.println("start");
@@ -155,7 +140,7 @@ public class DownloadTest {
public void finish() {
System.out.println("end");
}
- });
+ }).downloadFile(FileUtil.file("d:/download/temp"));
Assertions.assertNotNull(file);
Assertions.assertTrue(file.exists());
@@ -172,15 +157,14 @@ public class DownloadTest {
public void downloadFileFromUrlTest4() {
File file = null;
try {
- file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"), 1);
+ file = HttpDownloader.of("http://groovy-lang.org/changelogs/changelog-3.0.5.html")
+ .downloadFile(FileUtil.file("d:/download/temp"));
Assertions.assertNotNull(file);
Assertions.assertTrue(file.exists());
Assertions.assertTrue(file.isFile());
Assertions.assertTrue(file.length() > 0);
Assertions.assertFalse(file.getName().isEmpty());
- } catch (final Exception e) {
- Assertions.assertInstanceOf(IORuntimeException.class, e);
} finally {
FileUtil.del(file);
}
@@ -192,7 +176,8 @@ public class DownloadTest {
public void downloadFileFromUrlTest5() {
File file = null;
try {
- file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
+ file = HttpDownloader.of("http://groovy-lang.org/changelogs/changelog-3.0.5.html")
+ .downloadFile(FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
Assertions.assertNotNull(file);
Assertions.assertTrue(file.exists());
@@ -204,7 +189,8 @@ public class DownloadTest {
File file1 = null;
try {
- file1 = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"));
+ file1 = HttpDownloader.of("http://groovy-lang.org/changelogs/changelog-3.0.5.html")
+ .downloadFile(FileUtil.file("d:/download/temp"));
Assertions.assertNotNull(file1);
Assertions.assertTrue(file1.exists());
@@ -220,36 +206,10 @@ public class DownloadTest {
public void downloadTeamViewerTest() throws IOException {
// 此URL有3次重定向, 需要请求4次
final String url = "https://download.teamviewer.com/download/TeamViewer_Setup_x64.exe";
- HttpGlobalConfig.setMaxRedirects(20);
- final Path temp = Files.createTempFile("tmp", ".exe");
- final File file = HttpDownloader.downloadFile(url, temp.toFile());
- Console.log(file.length());
- }
+ HttpGlobalConfig.setMaxRedirects(2);
- @Test
- @Disabled
- public void downloadToStreamTest() {
- String url2 = "http://storage.chancecloud.com.cn/20200413_%E7%B2%A4B12313_386.pdf";
- final ByteArrayOutputStream os2 = new ByteArrayOutputStream();
- HttpDownloader.download(url2, os2, false, null);
-
- url2 = "http://storage.chancecloud.com.cn/20200413_粤B12313_386.pdf";
- HttpDownloader.download(url2, os2, false, null);
- }
-
- @Test
- @Disabled
- public void downloadStringTest() {
- final String url = "https://www.baidu.com";
- // 从远程直接读取字符串,需要自定义编码,直接调用JDK方法
- final String content2 = HttpDownloader.downloadString(url, CharsetUtil.UTF_8, null);
- Console.log(content2);
- }
-
- @Test
- @Disabled
- public void gimg2Test(){
- final byte[] bytes = HttpDownloader.downloadBytes("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea");
- Console.log(Base64.encode(bytes));
+ final Response send = Request.of(url).send(ClientEngineFactory.createEngine("jdkClient"));
+ Console.log(send.getStatus());
+ Console.log(send.headers());
}
}