|
|
@@ -1,14 +1,26 @@
|
|
|
package cn.hutool.http;
|
|
|
|
|
|
import cn.hutool.core.convert.Convert;
|
|
|
-import cn.hutool.core.io.*;
|
|
|
+import cn.hutool.core.io.FastByteArrayOutputStream;
|
|
|
+import cn.hutool.core.io.FileUtil;
|
|
|
+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 cn.hutool.core.util.CharsetUtil;
|
|
|
import cn.hutool.core.util.ReUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.core.util.URLUtil;
|
|
|
import cn.hutool.http.cookie.GlobalCookieManager;
|
|
|
|
|
|
-import java.io.*;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.Closeable;
|
|
|
+import java.io.EOFException;
|
|
|
+import java.io.File;
|
|
|
+import java.io.FileNotFoundException;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.io.OutputStream;
|
|
|
import java.net.HttpCookie;
|
|
|
import java.nio.charset.Charset;
|
|
|
import java.util.List;
|
|
|
@@ -17,32 +29,43 @@ import java.util.Map.Entry;
|
|
|
/**
|
|
|
* Http响应类<br>
|
|
|
* 非线程安全对象
|
|
|
- *
|
|
|
- * @author Looly
|
|
|
*
|
|
|
+ * @author Looly
|
|
|
*/
|
|
|
public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
- /** 持有连接对象 */
|
|
|
+ /**
|
|
|
+ * 持有连接对象
|
|
|
+ */
|
|
|
protected HttpConnection httpConnection;
|
|
|
- /** Http请求原始流 */
|
|
|
+ /**
|
|
|
+ * Http请求原始流
|
|
|
+ */
|
|
|
protected InputStream in;
|
|
|
- /** 是否异步,异步下只持有流,否则将在初始化时直接读取body内容 */
|
|
|
+ /**
|
|
|
+ * 是否异步,异步下只持有流,否则将在初始化时直接读取body内容
|
|
|
+ */
|
|
|
private volatile boolean isAsync;
|
|
|
- /** 响应状态码 */
|
|
|
+ /**
|
|
|
+ * 响应状态码
|
|
|
+ */
|
|
|
protected int status;
|
|
|
- /** 是否忽略读取Http响应体 */
|
|
|
+ /**
|
|
|
+ * 是否忽略读取Http响应体
|
|
|
+ */
|
|
|
private final boolean ignoreBody;
|
|
|
- /** 从响应中获取的编码 */
|
|
|
+ /**
|
|
|
+ * 从响应中获取的编码
|
|
|
+ */
|
|
|
private Charset charsetFromResponse;
|
|
|
|
|
|
/**
|
|
|
* 构造
|
|
|
- *
|
|
|
+ *
|
|
|
* @param httpConnection {@link HttpConnection}
|
|
|
- * @param charset 编码,从请求编码中获取默认编码
|
|
|
- * @param isAsync 是否异步
|
|
|
- * @param isIgnoreBody 是否忽略读取响应体
|
|
|
+ * @param charset 编码,从请求编码中获取默认编码
|
|
|
+ * @param isAsync 是否异步
|
|
|
+ * @param isIgnoreBody 是否忽略读取响应体
|
|
|
* @since 3.1.2
|
|
|
*/
|
|
|
protected HttpResponse(HttpConnection httpConnection, Charset charset, boolean isAsync, boolean isIgnoreBody) {
|
|
|
@@ -55,7 +78,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 获取状态码
|
|
|
- *
|
|
|
+ *
|
|
|
* @return 状态码
|
|
|
*/
|
|
|
public int getStatus() {
|
|
|
@@ -64,7 +87,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 请求是否成功,判断依据为:状态码范围在200~299内。
|
|
|
- *
|
|
|
+ *
|
|
|
* @return 是否成功请求
|
|
|
* @since 4.1.9
|
|
|
*/
|
|
|
@@ -76,7 +99,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
* 同步<br>
|
|
|
* 如果为异步状态,则暂时不读取服务器中响应的内容,而是持有Http链接的{@link InputStream}。<br>
|
|
|
* 当调用此方法时,异步状态转为同步状态,此时从Http链接流中读取body内容并暂存在内容中。如果已经是同步状态,则不进行任何操作。
|
|
|
- *
|
|
|
+ *
|
|
|
* @return this
|
|
|
*/
|
|
|
public HttpResponse sync() {
|
|
|
@@ -84,9 +107,10 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------- Http Response Header start
|
|
|
+
|
|
|
/**
|
|
|
* 获取内容编码
|
|
|
- *
|
|
|
+ *
|
|
|
* @return String
|
|
|
*/
|
|
|
public String contentEncoding() {
|
|
|
@@ -95,7 +119,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 是否为gzip压缩过的内容
|
|
|
- *
|
|
|
+ *
|
|
|
* @return 是否为gzip压缩过的内容
|
|
|
*/
|
|
|
public boolean isGzip() {
|
|
|
@@ -105,7 +129,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 是否为zlib(Defalte)压缩过的内容
|
|
|
- *
|
|
|
+ *
|
|
|
* @return 是否为zlib(Defalte)压缩过的内容
|
|
|
* @since 4.5.7
|
|
|
*/
|
|
|
@@ -116,7 +140,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 是否为Transfer-Encoding:Chunked的内容
|
|
|
- *
|
|
|
+ *
|
|
|
* @return 是否为Transfer-Encoding:Chunked的内容
|
|
|
* @since 4.6.2
|
|
|
*/
|
|
|
@@ -127,7 +151,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 获取本次请求服务器返回的Cookie信息
|
|
|
- *
|
|
|
+ *
|
|
|
* @return Cookie字符串
|
|
|
* @since 3.1.1
|
|
|
*/
|
|
|
@@ -137,7 +161,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 获取Cookie
|
|
|
- *
|
|
|
+ *
|
|
|
* @return Cookie列表
|
|
|
* @since 3.1.1
|
|
|
*/
|
|
|
@@ -147,7 +171,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 获取Cookie
|
|
|
- *
|
|
|
+ *
|
|
|
* @param name Cookie名
|
|
|
* @return {@link HttpCookie}
|
|
|
* @since 4.1.4
|
|
|
@@ -166,7 +190,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 获取Cookie值
|
|
|
- *
|
|
|
+ *
|
|
|
* @param name Cookie名
|
|
|
* @return Cookie值
|
|
|
* @since 4.1.4
|
|
|
@@ -178,12 +202,13 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
// ---------------------------------------------------------------- Http Response Header end
|
|
|
|
|
|
// ---------------------------------------------------------------- Body start
|
|
|
+
|
|
|
/**
|
|
|
* 获得服务区响应流<br>
|
|
|
* 异步模式下获取Http原生流,同步模式下获取获取到的在内存中的副本<br>
|
|
|
* 如果想在同步模式下获取流,请先调用{@link #sync()}方法强制同步<br>
|
|
|
* 流获取后处理完毕需关闭此类
|
|
|
- *
|
|
|
+ *
|
|
|
* @return 响应流
|
|
|
*/
|
|
|
public InputStream bodyStream() {
|
|
|
@@ -196,7 +221,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
/**
|
|
|
* 获取响应流字节码<br>
|
|
|
* 此方法会转为同步模式
|
|
|
- *
|
|
|
+ *
|
|
|
* @return byte[]
|
|
|
*/
|
|
|
public byte[] bodyBytes() {
|
|
|
@@ -206,7 +231,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 获取响应主体
|
|
|
- *
|
|
|
+ *
|
|
|
* @return String
|
|
|
* @throws HttpException 包装IO异常
|
|
|
*/
|
|
|
@@ -218,9 +243,9 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
* 将响应内容写出到{@link OutputStream}<br>
|
|
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
|
|
* 写出后会关闭Http流(异步模式)
|
|
|
- *
|
|
|
- * @param out 写出的流
|
|
|
- * @param isCloseOut 是否关闭输出流
|
|
|
+ *
|
|
|
+ * @param out 写出的流
|
|
|
+ * @param isCloseOut 是否关闭输出流
|
|
|
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
|
|
|
* @return 写出bytes数
|
|
|
* @since 3.3.2
|
|
|
@@ -238,7 +263,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 将响应内容写出到文件<br>
|
|
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
|
|
@@ -246,31 +271,25 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
*
|
|
|
* @param destFile 写出到的文件
|
|
|
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
|
|
|
- *
|
|
|
* @return 写出bytes数
|
|
|
- *
|
|
|
* @since 3.3.2
|
|
|
*/
|
|
|
public long writeBody(File destFile, StreamProgress streamProgress) {
|
|
|
- if (null == destFile) {
|
|
|
- throw new NullPointerException("[destFile] is null!");
|
|
|
- }
|
|
|
-
|
|
|
- File outFile = completeFileNameFromHeader(destFile);
|
|
|
- OutputStream outputStream = FileUtil.getOutputStream(outFile);
|
|
|
+ Assert.notNull(destFile, "[destFile] is null!");
|
|
|
+
|
|
|
+ final File outFile = completeFileNameFromHeader(destFile);
|
|
|
+ final OutputStream outputStream = FileUtil.getOutputStream(outFile);
|
|
|
return writeBody(outputStream, true, streamProgress);
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 将响应内容写出到文件<br>
|
|
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
|
|
* 写出后会关闭Http流(异步模式)
|
|
|
*
|
|
|
* @param destFile 写出到的文件
|
|
|
- *
|
|
|
* @return 写出bytes数
|
|
|
- *
|
|
|
* @since 3.3.2
|
|
|
*/
|
|
|
public long writeBody(File destFile) {
|
|
|
@@ -281,7 +300,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
* 将响应内容写出到文件<br>
|
|
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
|
|
* 写出后会关闭Http流(异步模式)
|
|
|
- *
|
|
|
+ *
|
|
|
* @param destFilePath 写出到的文件的路径
|
|
|
* @return 写出bytes数
|
|
|
* @since 3.3.2
|
|
|
@@ -298,7 +317,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
// 关闭连接
|
|
|
this.httpConnection.disconnectQuietly();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@Override
|
|
|
public String toString() {
|
|
|
StringBuilder sb = StrUtil.builder();
|
|
|
@@ -306,26 +325,26 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
for (Entry<String, List<String>> entry : this.headers.entrySet()) {
|
|
|
sb.append(" ").append(entry).append(StrUtil.CRLF);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
sb.append("Response Body: ").append(StrUtil.CRLF);
|
|
|
sb.append(" ").append(this.body()).append(StrUtil.CRLF);
|
|
|
-
|
|
|
+
|
|
|
return sb.toString();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 从响应头补全下载文件名
|
|
|
*
|
|
|
* @param destFile 目标文件夹或者目标文件
|
|
|
- *
|
|
|
* @return File 保存的文件
|
|
|
+ * @since 5.4.1
|
|
|
*/
|
|
|
public File completeFileNameFromHeader(File destFile) {
|
|
|
- if (!destFile.isDirectory()) {
|
|
|
+ if (false == destFile.isDirectory()) {
|
|
|
// 非目录直接返回
|
|
|
return destFile;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 从头信息中获取文件名
|
|
|
String fileName = getFileNameFromDisposition();
|
|
|
if (StrUtil.isBlank(fileName)) {
|
|
|
@@ -339,9 +358,9 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
}
|
|
|
return FileUtil.file(destFile, fileName);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// ---------------------------------------------------------------- Private method start
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 初始化Http响应,并在报错时关闭连接。<br>
|
|
|
* 初始化包括:
|
|
|
@@ -351,7 +370,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
* 2、读取头信息
|
|
|
* 3、持有Http流,并不关闭流
|
|
|
* </pre>
|
|
|
- *
|
|
|
+ *
|
|
|
* @return this
|
|
|
* @throws HttpException IO异常
|
|
|
*/
|
|
|
@@ -368,13 +387,13 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
/**
|
|
|
* 初始化Http响应<br>
|
|
|
* 初始化包括:
|
|
|
- *
|
|
|
+ *
|
|
|
* <pre>
|
|
|
* 1、读取Http状态
|
|
|
* 2、读取头信息
|
|
|
* 3、持有Http流,并不关闭流
|
|
|
* </pre>
|
|
|
- *
|
|
|
+ *
|
|
|
* @return this
|
|
|
* @throws HttpException IO异常
|
|
|
*/
|
|
|
@@ -417,7 +436,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
|
|
|
/**
|
|
|
* 读取主体,忽略EOFException异常
|
|
|
- *
|
|
|
+ *
|
|
|
* @param in 输入流
|
|
|
* @throws IORuntimeException IO异常
|
|
|
*/
|
|
|
@@ -444,14 +463,14 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
/**
|
|
|
* 强制同步,用于初始化<br>
|
|
|
* 强制同步后变化如下:
|
|
|
- *
|
|
|
+ *
|
|
|
* <pre>
|
|
|
* 1、读取body内容到内存
|
|
|
* 2、异步状态设为false(变为同步状态)
|
|
|
* 3、关闭Http流
|
|
|
* 4、断开与服务器连接
|
|
|
* </pre>
|
|
|
- *
|
|
|
+ *
|
|
|
* @return this
|
|
|
*/
|
|
|
private HttpResponse forceSync() {
|
|
|
@@ -473,7 +492,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
}
|
|
|
return this;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 从Content-Disposition头中获取文件名
|
|
|
*
|
|
|
@@ -490,6 +509,6 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|
|
}
|
|
|
return fileName;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// ---------------------------------------------------------------- Private method end
|
|
|
}
|