mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
Merge pull request #1023 from hdfg159/v5-dev
add HttpUtil download file return file
This commit is contained in:
commit
9a94f2e750
@ -1,25 +1,14 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
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.io.*;
|
||||
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.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.io.*;
|
||||
import java.net.HttpCookie;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
@ -249,46 +238,39 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将响应内容写出到文件<br>
|
||||
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
||||
* 写出后会关闭Http流(异步模式)
|
||||
*
|
||||
* @param destFile 写出到的文件
|
||||
*
|
||||
* @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!");
|
||||
}
|
||||
if (destFile.isDirectory()) {
|
||||
// 从头信息中获取文件名
|
||||
String fileName = getFileNameFromDisposition();
|
||||
if (StrUtil.isBlank(fileName)) {
|
||||
final String path = this.httpConnection.getUrl().getPath();
|
||||
// 从路径中获取文件名
|
||||
fileName = StrUtil.subSuf(path, path.lastIndexOf('/') + 1);
|
||||
if (StrUtil.isBlank(fileName)) {
|
||||
// 编码后的路径做为文件名
|
||||
fileName = URLUtil.encodeQuery(path, CharsetUtil.CHARSET_UTF_8);
|
||||
}
|
||||
}
|
||||
destFile = FileUtil.file(destFile, fileName);
|
||||
}
|
||||
|
||||
return writeBody(FileUtil.getOutputStream(destFile), true, streamProgress);
|
||||
|
||||
File outFile = completeFileNameFromHeader(destFile);
|
||||
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) {
|
||||
@ -316,7 +298,7 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
||||
// 关闭连接
|
||||
this.httpConnection.disconnectQuietly();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = StrUtil.builder();
|
||||
@ -324,18 +306,46 @@ 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 保存的文件
|
||||
*/
|
||||
public File completeFileNameFromHeader(File destFile) {
|
||||
if (!destFile.isDirectory()) {
|
||||
// 非目录直接返回
|
||||
return destFile;
|
||||
}
|
||||
|
||||
// 从头信息中获取文件名
|
||||
String fileName = getFileNameFromDisposition();
|
||||
if (StrUtil.isBlank(fileName)) {
|
||||
final String path = httpConnection.getUrl().getPath();
|
||||
// 从路径中获取文件名
|
||||
fileName = StrUtil.subSuf(path, path.lastIndexOf('/') + 1);
|
||||
if (StrUtil.isBlank(fileName)) {
|
||||
// 编码后的路径做为文件名
|
||||
fileName = URLUtil.encodeQuery(path, CharsetUtil.CHARSET_UTF_8);
|
||||
}
|
||||
}
|
||||
return FileUtil.file(destFile, fileName);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Private method start
|
||||
|
||||
/**
|
||||
* 初始化Http响应,并在报错时关闭连接。<br>
|
||||
* 初始化包括:
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* 1、读取Http状态
|
||||
* 2、读取头信息
|
||||
@ -463,10 +473,10 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从Content-Disposition头中获取文件名
|
||||
*
|
||||
*
|
||||
* @return 文件名,empty表示无
|
||||
*/
|
||||
private String getFileNameFromDisposition() {
|
||||
@ -480,5 +490,6 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Private method end
|
||||
}
|
||||
|
@ -8,11 +8,7 @@ import cn.hutool.core.io.StreamProgress;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.net.url.UrlQuery;
|
||||
import cn.hutool.core.text.StrBuilder;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.http.server.SimpleServer;
|
||||
|
||||
import java.io.File;
|
||||
@ -314,6 +310,88 @@ public class HttpUtil {
|
||||
* @since 4.0.4
|
||||
*/
|
||||
public static long downloadFile(String url, File destFile, int timeout, StreamProgress streamProgress) {
|
||||
HttpResponse response = requestDownloadFile(url, destFile, timeout);
|
||||
return response.writeBody(destFile, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param dest 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
*
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFileFromUrl(String url, String dest) {
|
||||
return downloadFileFromUrl(url, FileUtil.file(dest));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
*
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFileFromUrl(String url, File destFile) {
|
||||
return downloadFileFromUrl(url, destFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
*
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFileFromUrl(String url, File destFile, int timeout) {
|
||||
return downloadFileFromUrl(url, destFile, timeout, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param streamProgress 进度条
|
||||
*
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFileFromUrl(String url, File destFile, StreamProgress streamProgress) {
|
||||
return downloadFileFromUrl(url, destFile, -1, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @param streamProgress 进度条
|
||||
*
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFileFromUrl(String url, File destFile, int timeout, StreamProgress streamProgress) {
|
||||
HttpResponse response = requestDownloadFile(url, destFile, timeout);
|
||||
|
||||
File outFile = response.completeFileNameFromHeader(destFile);
|
||||
long writeBytes = response.writeBody(outFile, streamProgress);
|
||||
return outFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求下载文件
|
||||
*
|
||||
* @param url 请求下载文件地址
|
||||
* @param destFile 目标目录或者目标文件
|
||||
* @param timeout 超时时间
|
||||
*
|
||||
* @return HttpResponse
|
||||
*/
|
||||
private static HttpResponse requestDownloadFile(String url, File destFile, int timeout) {
|
||||
if (StrUtil.isBlank(url)) {
|
||||
throw new NullPointerException("[url] is null!");
|
||||
}
|
||||
@ -321,18 +399,19 @@ public class HttpUtil {
|
||||
throw new NullPointerException("[destFile] is null!");
|
||||
}
|
||||
final HttpResponse response = HttpRequest.get(url).timeout(timeout).executeAsync();
|
||||
if (false == response.isOk()) {
|
||||
if (!response.isOk()) {
|
||||
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
|
||||
}
|
||||
return response.writeBody(destFile, streamProgress);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param out 将下载内容写到输出流中 {@link OutputStream}
|
||||
* @param isCloseOut 是否关闭输出流
|
||||
*
|
||||
* @return 文件大小
|
||||
*/
|
||||
public static long download(String url, OutputStream out, boolean isCloseOut) {
|
||||
@ -355,29 +434,31 @@ public class HttpUtil {
|
||||
if (null == out) {
|
||||
throw new NullPointerException("[out] is null!");
|
||||
}
|
||||
|
||||
|
||||
final HttpResponse response = HttpRequest.get(url).executeAsync();
|
||||
if (false == response.isOk()) {
|
||||
if (!response.isOk()) {
|
||||
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
|
||||
}
|
||||
return response.writeBody(out, isCloseOut, streamProgress);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下载远程文件数据,支持30x跳转
|
||||
*
|
||||
* @param url 请求的url
|
||||
*
|
||||
* @return 文件数据
|
||||
*
|
||||
* @since 5.3.6
|
||||
*/
|
||||
public static byte[] downloadBytes(String url) {
|
||||
if (StrUtil.isBlank(url)) {
|
||||
throw new NullPointerException("[url] is null!");
|
||||
}
|
||||
|
||||
|
||||
final HttpResponse response = HttpRequest.get(url)
|
||||
.setFollowRedirects(true).executeAsync();
|
||||
if (false == response.isOk()) {
|
||||
if (!response.isOk()) {
|
||||
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
|
||||
}
|
||||
return response.bodyBytes();
|
||||
|
@ -1,13 +1,17 @@
|
||||
package cn.hutool.http.test;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.StreamProgress;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 下载单元测试
|
||||
@ -56,11 +60,134 @@ public class DownloadTest {
|
||||
long speed = progressSize / (System.currentTimeMillis() - time) * 1000;
|
||||
Console.log("已下载:{}, 速度:{}/s", FileUtil.readableFileSize(progressSize), FileUtil.readableFileSize(speed));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
Console.log("下载完成!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadFileFromUrlTest1() {
|
||||
File file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", "d:/download/temp");
|
||||
Assert.assertNotNull(file);
|
||||
Assert.assertTrue(file.isFile());
|
||||
Assert.assertTrue(file.length() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadFileFromUrlTest2() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpUtil.downloadFileFromUrl("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() {
|
||||
@Override
|
||||
public void start() {
|
||||
System.out.println("start");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progress(long progressSize) {
|
||||
System.out.println("download size:" + progressSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
System.out.println("end");
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertNotNull(file);
|
||||
Assert.assertTrue(file.exists());
|
||||
Assert.assertTrue(file.isFile());
|
||||
Assert.assertTrue(file.length() > 0);
|
||||
Assert.assertTrue(file.getName().length() > 0);
|
||||
} catch (Exception e) {
|
||||
Assert.assertTrue(e instanceof IORuntimeException);
|
||||
} finally {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadFileFromUrlTest3() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpUtil.downloadFileFromUrl("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), new StreamProgress() {
|
||||
@Override
|
||||
public void start() {
|
||||
System.out.println("start");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progress(long progressSize) {
|
||||
System.out.println("download size:" + progressSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
System.out.println("end");
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertNotNull(file);
|
||||
Assert.assertTrue(file.exists());
|
||||
Assert.assertTrue(file.isFile());
|
||||
Assert.assertTrue(file.length() > 0);
|
||||
Assert.assertTrue(file.getName().length() > 0);
|
||||
} finally {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadFileFromUrlTest4() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"), 1);
|
||||
|
||||
Assert.assertNotNull(file);
|
||||
Assert.assertTrue(file.exists());
|
||||
Assert.assertTrue(file.isFile());
|
||||
Assert.assertTrue(file.length() > 0);
|
||||
Assert.assertTrue(file.getName().length() > 0);
|
||||
} catch (Exception e) {
|
||||
Assert.assertTrue(e instanceof IORuntimeException);
|
||||
} finally {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadFileFromUrlTest5() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
|
||||
|
||||
Assert.assertNotNull(file);
|
||||
Assert.assertTrue(file.exists());
|
||||
Assert.assertTrue(file.isFile());
|
||||
Assert.assertTrue(file.length() > 0);
|
||||
} finally {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
|
||||
File file1 = null;
|
||||
try {
|
||||
file1 = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"));
|
||||
|
||||
Assert.assertNotNull(file1);
|
||||
Assert.assertTrue(file1.exists());
|
||||
Assert.assertTrue(file1.isFile());
|
||||
Assert.assertTrue(file1.length() > 0);
|
||||
} finally {
|
||||
FileUtil.del(file1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user