最近对接易企签,需要在用户签署完后把签名文件下载并存储到我们自己的文件存储中心,本来在测试环境和预发环境测试都都无问题,可是,上生产后,问题就来了

最开始下载文件的代码很简单

1
2
3
4

URL url = new URL(fileUrl);
InputStream is = url.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(is, baos);

URL url = new URL(fileUrl);
InputStream is = url.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(is, baos);

测试环境中,易企签给的文件地址可以直接返回文件流,这种方式就下载小文件自然就没什么问题,可是,当发布到生产后,直接通过上面的代码就只能获取到下面这样的代码

1

Found. Redirecting to http://open.signit.cn/v1/file/public/resources/00941ed4-bd2a-4c82-8758-2eb3760b13ee?token=6777e9df-4068-4e57-9de3-37a9387f5170&resourceName=h07Ah6zzLvtmQ6CL4s4D3qin.pdf

Found. Redirecting to http://open.signit.cn/v1/file/public/resources/00941ed4-bd2a-4c82-8758-2eb3760b13ee?token=6777e9df-4068-4e57-9de3-37a9387f5170&resourceName=h07Ah6zzLvtmQ6CL4s4D3qin.pdf

很明显,给的文件地址不再像预发环境那般单纯了,它会302重定向,这就不好意思了,上面的代码不能用,需要加强一下代码逻辑,判断响应状态码如果是302,再从请求头Location中取地址进行数据流的获取。正准备自己来处理的,突然想到项目中已经引入了强大的Hutool工具包,然后一句代码就OK了

byte] fileByte = HttpUtil.downloadBytes(fileUrl);

1

byte[] fileByte = HttpUtil.downloadBytes(fileUrl);

那么,为什么它一行代码就可以处理这个难题呢?我们来大概地看一下,首先进入到downloadBytes中

public static byte[] downloadBytes(String url) {
return HttpDownloader.downloadBytes(url);
}

1
2
3

public static byte[] downloadBytes(String url) {
return HttpDownloader.downloadBytes(url);
}

最终可以跟到HttpUtil.createGet这个方法中

public static HttpRequest createGet(String url, boolean isFollowRedirects) {
return HttpRequest.get(url).setFollowRedirects(isFollowRedirects);
}
this.httpConnection = HttpConnection
.create(this.url.toURL(this.urlHandler), this.proxy)//
.setConnectTimeout(this.connectionTimeout)//
.setReadTimeout(this.readTimeout)//
.setMethod(this.method)//
.setHttpsInfo(this.hostnameVerifier, this.ssf)//
// 定义转发
.setInstanceFollowRedirects(this.maxRedirectCount > 0)
// 流方式上传数据
.setChunkedStreamingMode(this.blockSize)
// 覆盖默认Header
.header(this.headers, true);
public HttpResponse execute(boolean isAsync) {
// 初始化URL
urlWithParamIfGet();
// 初始化 connection
initConnection();
// 发送请求
send();
// 手动实现重定向
HttpResponse httpResponse = sendRedirectIfPossible();
// 获取响应
if (null == httpResponse) {
httpResponse = new HttpResponse(this.httpConnection, this.charset, isAsync, isIgnoreResponseBody());
}
return httpResponse;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

public static HttpRequest createGet(String url, boolean isFollowRedirects) {
return HttpRequest.get(url).setFollowRedirects(isFollowRedirects);
}
this.httpConnection = HttpConnection
.create(this.url.toURL(this.urlHandler), this.proxy)//
.setConnectTimeout(this.connectionTimeout)//
.setReadTimeout(this.readTimeout)//
.setMethod(this.method)//
.setHttpsInfo(this.hostnameVerifier, this.ssf)//
// 定义转发
.setInstanceFollowRedirects(this.maxRedirectCount > 0)
// 流方式上传数据
.setChunkedStreamingMode(this.blockSize)
// 覆盖默认Header
.header(this.headers, true);
public HttpResponse execute(boolean isAsync) {
// 初始化URL
urlWithParamIfGet();
// 初始化 connection
initConnection();
// 发送请求
send();
// 手动实现重定向
HttpResponse httpResponse = sendRedirectIfPossible();
// 获取响应
if (null == httpResponse) {
httpResponse = new HttpResponse(this.httpConnection, this.charset, isAsync, isIgnoreResponseBody());
}
return httpResponse;
}

进行URL初始化后,手动设置重定向,其中maxRedirectCount是可重定向的次数,默认是2次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

private HttpResponse sendRedirectIfPossible() {
if (this.maxRedirectCount < 1) {
// 不重定向
return null;
}
// 手动实现重定向
if (this.httpConnection.getHttpURLConnection().getInstanceFollowRedirects()) {
int responseCode;
try {
responseCode = httpConnection.responseCode();
} catch (IOException e) {
// 错误时静默关闭连接
this.httpConnection.disconnectQuietly();
throw new HttpException(e);
}
if (responseCode != HttpURLConnection.HTTP_OK) {
if (HttpStatus.isRedirected(responseCode)) {
setUrl(httpConnection.header(Header.LOCATION));
if (redirectCount < this.maxRedirectCount) {
redirectCount++;
return execute();
}
}
}
}
return null;
}

private HttpResponse sendRedirectIfPossible() {
if (this.maxRedirectCount < 1) {
// 不重定向
return null;
}
// 手动实现重定向
if (this.httpConnection.getHttpURLConnection().getInstanceFollowRedirects()) {
int responseCode;
try {
responseCode = httpConnection.responseCode();
} catch (IOException e) {
// 错误时静默关闭连接
this.httpConnection.disconnectQuietly();
throw new HttpException(e);
}
if (responseCode != HttpURLConnection.HTTP_OK) {
if (HttpStatus.isRedirected(responseCode)) {
setUrl(httpConnection.header(Header.LOCATION));
if (redirectCount < this.maxRedirectCount) {
redirectCount++;
return execute();
}
}
}
}
return null;
}

其实它的实现方式也是上面我们提到的思路是一致的,通过递归方式去多次获取返回的请求头中的LOCATION地址,尝试获取最终的文件下载地址。现在这般简便的工具类越发的多,我们在使用的同时也需要去学习工具类的实现思路。这样我们才不会越发被动。