mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
add HttpInputStream
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
* 【core】 强化ExceptionUtil(issue#459@Github)
|
* 【core】 强化ExceptionUtil(issue#459@Github)
|
||||||
* 【core】 增强日期工具类(pr#455@Github)
|
* 【core】 增强日期工具类(pr#455@Github)
|
||||||
* 【setting】 构造Setting增加默认字符编码
|
* 【setting】 构造Setting增加默认字符编码
|
||||||
|
* 【extra】 ServletUtil增加getHeaderMap方法
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【cache】 修复missCount规则(issue#465@Github)
|
* 【cache】 修复missCount规则(issue#465@Github)
|
||||||
|
@@ -55,10 +55,10 @@ public class WatchMonitor extends Thread implements Closeable, Serializable{
|
|||||||
public static final WatchEvent.Kind<?> ENTRY_DELETE = StandardWatchEventKinds.ENTRY_DELETE;
|
public static final WatchEvent.Kind<?> ENTRY_DELETE = StandardWatchEventKinds.ENTRY_DELETE;
|
||||||
/** 全部事件 */
|
/** 全部事件 */
|
||||||
public static final WatchEvent.Kind<?>[] EVENTS_ALL = {//
|
public static final WatchEvent.Kind<?>[] EVENTS_ALL = {//
|
||||||
StandardWatchEventKinds.OVERFLOW, //事件丢失
|
OVERFLOW, //事件丢失
|
||||||
StandardWatchEventKinds.ENTRY_MODIFY, //修改
|
ENTRY_MODIFY, //修改
|
||||||
StandardWatchEventKinds.ENTRY_CREATE, //创建
|
ENTRY_CREATE, //创建
|
||||||
StandardWatchEventKinds.ENTRY_DELETE //删除
|
ENTRY_DELETE //删除
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 监听路径,必须为目录 */
|
/** 监听路径,必须为目录 */
|
||||||
|
@@ -5,9 +5,9 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
@@ -267,6 +267,27 @@ public class ServletUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------- Header start
|
// --------------------------------------------------------- Header start
|
||||||
|
/**
|
||||||
|
* 获取请求所有的头(header)信息
|
||||||
|
*
|
||||||
|
* @param request 请求对象{@link HttpServletRequest}
|
||||||
|
* @return header值
|
||||||
|
* @since 4.6.2
|
||||||
|
*/
|
||||||
|
public static Map<String, String> getHeaderMap(HttpServletRequest request) {
|
||||||
|
final Map<String, String> headerMap = new HashMap<>();
|
||||||
|
|
||||||
|
final Enumeration<String> names = request.getHeaderNames();
|
||||||
|
String name = null;
|
||||||
|
while (names.hasMoreElements()) {
|
||||||
|
name = names.nextElement();
|
||||||
|
headerMap.put(name, request.getHeader(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 忽略大小写获得请求header中的信息
|
* 忽略大小写获得请求header中的信息
|
||||||
*
|
*
|
||||||
@@ -274,8 +295,8 @@ public class ServletUtil {
|
|||||||
* @param nameIgnoreCase 忽略大小写头信息的KEY
|
* @param nameIgnoreCase 忽略大小写头信息的KEY
|
||||||
* @return header值
|
* @return header值
|
||||||
*/
|
*/
|
||||||
public final static String getHeaderIgnoreCase(HttpServletRequest request, String nameIgnoreCase) {
|
public static String getHeaderIgnoreCase(HttpServletRequest request, String nameIgnoreCase) {
|
||||||
Enumeration<String> names = request.getHeaderNames();
|
final Enumeration<String> names = request.getHeaderNames();
|
||||||
String name = null;
|
String name = null;
|
||||||
while (names.hasMoreElements()) {
|
while (names.hasMoreElements()) {
|
||||||
name = names.nextElement();
|
name = names.nextElement();
|
||||||
@@ -286,6 +307,18 @@ public class ServletUtil {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得请求header中的信息
|
||||||
|
*
|
||||||
|
* @param request 请求对象{@link HttpServletRequest}
|
||||||
|
* @param name 头信息的KEY
|
||||||
|
* @param charsetName 字符集
|
||||||
|
* @return header值
|
||||||
|
*/
|
||||||
|
public static String getHeader(HttpServletRequest request, String name, String charsetName) {
|
||||||
|
return getHeader(request, name, CharsetUtil.charset(charsetName));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得请求header中的信息
|
* 获得请求header中的信息
|
||||||
@@ -294,15 +327,12 @@ public class ServletUtil {
|
|||||||
* @param name 头信息的KEY
|
* @param name 头信息的KEY
|
||||||
* @param charset 字符集
|
* @param charset 字符集
|
||||||
* @return header值
|
* @return header值
|
||||||
|
* @since 4.6.2
|
||||||
*/
|
*/
|
||||||
public final static String getHeader(HttpServletRequest request, String name, String charset) {
|
public static String getHeader(HttpServletRequest request, String name, Charset charset) {
|
||||||
final String header = request.getHeader(name);
|
final String header = request.getHeader(name);
|
||||||
if (null != header) {
|
if (null != header) {
|
||||||
try {
|
return CharsetUtil.convert(header, CharsetUtil.CHARSET_ISO_8859_1, charset);
|
||||||
return new String(header.getBytes(CharsetUtil.ISO_8859_1), charset);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new UtilException(StrUtil.format("Error charset {} for http request header.", charset));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -375,7 +405,7 @@ public class ServletUtil {
|
|||||||
* @param name cookie名
|
* @param name cookie名
|
||||||
* @return Cookie对象
|
* @return Cookie对象
|
||||||
*/
|
*/
|
||||||
public final static Cookie getCookie(HttpServletRequest httpServletRequest, String name) {
|
public static Cookie getCookie(HttpServletRequest httpServletRequest, String name) {
|
||||||
final Map<String, Cookie> cookieMap = readCookieMap(httpServletRequest);
|
final Map<String, Cookie> cookieMap = readCookieMap(httpServletRequest);
|
||||||
return cookieMap == null ? null : cookieMap.get(name);
|
return cookieMap == null ? null : cookieMap.get(name);
|
||||||
}
|
}
|
||||||
@@ -386,7 +416,7 @@ public class ServletUtil {
|
|||||||
* @param httpServletRequest {@link HttpServletRequest}
|
* @param httpServletRequest {@link HttpServletRequest}
|
||||||
* @return Cookie map
|
* @return Cookie map
|
||||||
*/
|
*/
|
||||||
public final static Map<String, Cookie> readCookieMap(HttpServletRequest httpServletRequest) {
|
public static Map<String, Cookie> readCookieMap(HttpServletRequest httpServletRequest) {
|
||||||
Map<String, Cookie> cookieMap = new HashMap<String, Cookie>();
|
Map<String, Cookie> cookieMap = new HashMap<String, Cookie>();
|
||||||
Cookie[] cookies = httpServletRequest.getCookies();
|
Cookie[] cookies = httpServletRequest.getCookies();
|
||||||
if (null == cookies) {
|
if (null == cookies) {
|
||||||
@@ -404,7 +434,7 @@ public class ServletUtil {
|
|||||||
* @param response 响应对象{@link HttpServletResponse}
|
* @param response 响应对象{@link HttpServletResponse}
|
||||||
* @param cookie Servlet Cookie对象
|
* @param cookie Servlet Cookie对象
|
||||||
*/
|
*/
|
||||||
public final static void addCookie(HttpServletResponse response, Cookie cookie) {
|
public static void addCookie(HttpServletResponse response, Cookie cookie) {
|
||||||
response.addCookie(cookie);
|
response.addCookie(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +445,7 @@ public class ServletUtil {
|
|||||||
* @param name Cookie名
|
* @param name Cookie名
|
||||||
* @param value Cookie值
|
* @param value Cookie值
|
||||||
*/
|
*/
|
||||||
public final static void addCookie(HttpServletResponse response, String name, String value) {
|
public static void addCookie(HttpServletResponse response, String name, String value) {
|
||||||
response.addCookie(new Cookie(name, value));
|
response.addCookie(new Cookie(name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +459,7 @@ public class ServletUtil {
|
|||||||
* @param path Cookie的有效路径
|
* @param path Cookie的有效路径
|
||||||
* @param domain the domain name within which this cookie is visible; form is according to RFC 2109
|
* @param domain the domain name within which this cookie is visible; form is according to RFC 2109
|
||||||
*/
|
*/
|
||||||
public final static void addCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds, String path, String domain) {
|
public static void addCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds, String path, String domain) {
|
||||||
Cookie cookie = new Cookie(name, value);
|
Cookie cookie = new Cookie(name, value);
|
||||||
if (domain != null) {
|
if (domain != null) {
|
||||||
cookie.setDomain(domain);
|
cookie.setDomain(domain);
|
||||||
@@ -449,7 +479,7 @@ public class ServletUtil {
|
|||||||
* @param value cookie值
|
* @param value cookie值
|
||||||
* @param maxAgeInSeconds -1: 关闭浏览器清除Cookie. 0: 立即清除Cookie. >0 : Cookie存在的秒数.
|
* @param maxAgeInSeconds -1: 关闭浏览器清除Cookie. 0: 立即清除Cookie. >0 : Cookie存在的秒数.
|
||||||
*/
|
*/
|
||||||
public final static void addCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) {
|
public static void addCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) {
|
||||||
addCookie(response, name, value, maxAgeInSeconds, "/", null);
|
addCookie(response, name, value, maxAgeInSeconds, "/", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@ import cn.hutool.extra.tokenizer.TokenizerException;
|
|||||||
public class AnalysisEngine implements TokenizerEngine {
|
public class AnalysisEngine implements TokenizerEngine {
|
||||||
|
|
||||||
private Analyzer analyzer;
|
private Analyzer analyzer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
|
@@ -4,8 +4,8 @@ import org.ansj.splitWord.Analysis;
|
|||||||
import org.ansj.splitWord.analysis.ToAnalysis;
|
import org.ansj.splitWord.analysis.ToAnalysis;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.tokenizer.TokenizerEngine;
|
|
||||||
import cn.hutool.extra.tokenizer.Result;
|
import cn.hutool.extra.tokenizer.Result;
|
||||||
|
import cn.hutool.extra.tokenizer.TokenizerEngine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ansj分词引擎实现<br>
|
* Ansj分词引擎实现<br>
|
||||||
@@ -38,5 +38,5 @@ public class AnsjEngine implements TokenizerEngine {
|
|||||||
public Result parse(CharSequence text) {
|
public Result parse(CharSequence text) {
|
||||||
return new AnsjResult(analysis.parseStr(StrUtil.str(text)));
|
return new AnsjResult(analysis.parseStr(StrUtil.str(text)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -39,5 +39,5 @@ public class HanLPEngine implements TokenizerEngine {
|
|||||||
public Result parse(CharSequence text) {
|
public Result parse(CharSequence text) {
|
||||||
return new HanLPResult(this.seg.seg(StrUtil.str(text)));
|
return new HanLPResult(this.seg.seg(StrUtil.str(text)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -39,5 +39,4 @@ public class IKAnalyzerEngine implements TokenizerEngine {
|
|||||||
this.seg.reset(StrUtil.getReader(text));
|
this.seg.reset(StrUtil.getReader(text));
|
||||||
return new IKAnalyzerResult(this.seg);
|
return new IKAnalyzerResult(this.seg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
111
hutool-http/src/main/java/cn/hutool/http/HttpInputStream.java
Normal file
111
hutool-http/src/main/java/cn/hutool/http/HttpInputStream.java
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package cn.hutool.http;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.DeflaterInputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP输入流,此流用于包装Http请求响应内容的流,用于解析各种压缩、分段的响应流内容
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HttpInputStream extends InputStream {
|
||||||
|
|
||||||
|
/** 原始流 */
|
||||||
|
private volatile InputStream in;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param response 响应对象
|
||||||
|
*/
|
||||||
|
public HttpInputStream(HttpResponse response) {
|
||||||
|
init(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return this.in.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
return this.in.read(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
return this.in.skip(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
return this.in.available();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
this.in.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void mark(int readlimit) {
|
||||||
|
this.in.mark(readlimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() throws IOException {
|
||||||
|
this.in.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return this.in.markSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化流
|
||||||
|
*
|
||||||
|
* @param response 响应对象
|
||||||
|
*/
|
||||||
|
private void init(HttpResponse response) {
|
||||||
|
try {
|
||||||
|
this.in = (response.status < HttpStatus.HTTP_BAD_REQUEST) ? response.httpConnection.getInputStream() : response.httpConnection.getErrorStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (e instanceof FileNotFoundException) {
|
||||||
|
// 服务器无返回内容,忽略之
|
||||||
|
} else {
|
||||||
|
throw new HttpException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在一些情况下,返回的流为null,此时提供状态码说明
|
||||||
|
if (null == this.in) {
|
||||||
|
this.in = new ByteArrayInputStream(StrUtil.format("Error request, response status: {}", response.status).getBytes());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 分段响应内容解析
|
||||||
|
if(response.isChunked()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.isGzip() && false == (response.in instanceof GZIPInputStream)) {
|
||||||
|
// Accept-Encoding: gzip
|
||||||
|
try {
|
||||||
|
this.in = new GZIPInputStream(this.in);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 在类似于Head等方法中无body返回,此时GZIPInputStream构造会出现错误,在此忽略此错误读取普通数据
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else if (response.isDeflate() && false == (this.in instanceof DeflaterInputStream)) {
|
||||||
|
// Accept-Encoding: defalte
|
||||||
|
this.in = new DeflaterInputStream(this.in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -12,8 +12,6 @@ import java.net.HttpCookie;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.zip.DeflaterInputStream;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.io.FastByteArrayOutputStream;
|
import cn.hutool.core.io.FastByteArrayOutputStream;
|
||||||
@@ -38,13 +36,13 @@ import cn.hutool.log.StaticLog;
|
|||||||
public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
||||||
|
|
||||||
/** 持有连接对象 */
|
/** 持有连接对象 */
|
||||||
private HttpConnection httpConnection;
|
protected HttpConnection httpConnection;
|
||||||
/** Http请求原始流 */
|
/** Http请求原始流 */
|
||||||
private InputStream in;
|
protected InputStream in;
|
||||||
/** 是否异步,异步下只持有流,否则将在初始化时直接读取body内容 */
|
/** 是否异步,异步下只持有流,否则将在初始化时直接读取body内容 */
|
||||||
private volatile boolean isAsync;
|
private volatile boolean isAsync;
|
||||||
/** 响应状态码 */
|
/** 响应状态码 */
|
||||||
private int status;
|
protected int status;
|
||||||
/** 是否忽略读取Http响应体 */
|
/** 是否忽略读取Http响应体 */
|
||||||
private boolean ignoreBody;
|
private boolean ignoreBody;
|
||||||
/** 从响应中获取的编码 */
|
/** 从响应中获取的编码 */
|
||||||
@@ -373,9 +371,9 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|||||||
* @throws HttpException IO异常
|
* @throws HttpException IO异常
|
||||||
*/
|
*/
|
||||||
private HttpResponse init() throws HttpException {
|
private HttpResponse init() throws HttpException {
|
||||||
|
// 获取响应状态码
|
||||||
try {
|
try {
|
||||||
this.status = httpConnection.responseCode();
|
this.status = httpConnection.responseCode();
|
||||||
this.in = (this.status < HttpStatus.HTTP_BAD_REQUEST) ? this.httpConnection.getInputStream() : this.httpConnection.getErrorStream();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (e instanceof FileNotFoundException) {
|
if (e instanceof FileNotFoundException) {
|
||||||
// 服务器无返回内容,忽略之
|
// 服务器无返回内容,忽略之
|
||||||
@@ -394,31 +392,15 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
|||||||
// 存储服务端设置的Cookie信息
|
// 存储服务端设置的Cookie信息
|
||||||
GlobalCookieManager.store(httpConnection);
|
GlobalCookieManager.store(httpConnection);
|
||||||
|
|
||||||
|
// 获取响应编码
|
||||||
final Charset charset = httpConnection.getCharset();
|
final Charset charset = httpConnection.getCharset();
|
||||||
this.charsetFromResponse = charset;
|
this.charsetFromResponse = charset;
|
||||||
if (null != charset) {
|
if (null != charset) {
|
||||||
this.charset = charset;
|
this.charset = charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null == this.in) {
|
// 获取响应内容流
|
||||||
// 在一些情况下,返回的流为null,此时提供状态码说明
|
this.in = new HttpInputStream(this);
|
||||||
this.in = new ByteArrayInputStream(StrUtil.format("Error request, response status: {}", this.status).getBytes());
|
|
||||||
} else {
|
|
||||||
// TODO 分段响应内容解析
|
|
||||||
|
|
||||||
if (isGzip() && false == (in instanceof GZIPInputStream)) {
|
|
||||||
// Accept-Encoding: gzip
|
|
||||||
try {
|
|
||||||
in = new GZIPInputStream(in);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// 在类似于Head等方法中无body返回,此时GZIPInputStream构造会出现错误,在此忽略此错误读取普通数据
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
} else if (isDeflate() && false == (in instanceof DeflaterInputStream)) {
|
|
||||||
// Accept-Encoding: defalte
|
|
||||||
in = new DeflaterInputStream(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 同步情况下强制同步
|
// 同步情况下强制同步
|
||||||
return this.isAsync ? this : forceSync();
|
return this.isAsync ? this : forceSync();
|
||||||
|
@@ -63,7 +63,8 @@ public class HttpUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void getTest5() {
|
public void getTest5() {
|
||||||
String res = HttpUtil.get("https://comment.bilibili.com/67573272.xml");
|
String res = HttpRequest.get("https://comment.bilibili.com/67573272.xml")
|
||||||
|
.execute().body();
|
||||||
Console.log(res);
|
Console.log(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user