下载器通常需要从指定的URL下载文件,并且保存到本地。可能还需要处理一些异常情况,比如网络问题、文件不存在等等。
首先,HttpClient是在Java 11中引入的,所以用户需要确认他们的Java版本是否至少是11。如果用户用的是旧版本,可能需要使用旧的HttpClient或者其他库,比如Apache HttpClient。但用户明确提到用Java的HttpClient库,所以假设他们用的是Java 11或更高版本。
接下来,我需要考虑如何用HttpClient发送GET请求获取文件。HttpClient的基本步骤是创建客户端实例,构造请求,发送请求,然后处理响应。对于下载文件,可能需要获取输入流,然后读取数据写入本地文件。

然后,保存文件的部分。需要从URL中提取文件名,或者如果URL路径中没有文件名,可能需要处理Content-Disposition头来获取文件名。但这个问题可能比较复杂,用户可能只需要一个简单的例子,所以暂时先假设URL路径中有文件名,这样可以通过解析URL路径来获取文件名。
接下来,需要考虑大文件的下载,可能需要使用流式处理,避免将整个文件加载到内存中。使用InputStream读取响应体,然后通过Files.copy或者手动读取字节并写入文件。
异常处理方面,需要处理IOException,可能还有URISyntaxException,如果URL格式不正确的话。此外,还要检查HTTP响应码是否为200,否则可能文件不存在或服务器错误。
然后,用户可能需要一个示例程序,包含main方法,接受URL参数,调用下载方法。例如,用户运行程序时输入URL,程序下载文件到当前目录。
可能还需要进度显示,比如显示下载的百分比,但用户没有特别提到,可能属于进阶功能。不过如果可能的话,可以简单提一下如何实现进度跟踪,比如通过监听读取的字节数。
还有需要考虑的是设置超时,防止长时间等待。HttpClient可以设置连接超时和请求超时。
另外,可能需要处理重定向,默认情况下HttpClient会自动处理重定向,所以用户可能不需要额外设置,但可以提到这一点。
以下是一个使用Java HttpClient实现的文件下载器示例,包含进度显示和基础错误处理:
java
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Duration;
public class FileDownloader {
private static final HttpClient httpClient = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(15))
.build();
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("请提供下载URL作为参数");
return;
}
try {
String url = args[0];
Path outputDir = args.length > 1 ? Path.of(args[1]) : Path.of("");
downloadFile(url, outputDir);
} catch (Exception e) {
System.err.println("下载失败: " + e.getMessage());
}
}
public static void downloadFile(String urlString, Path outputDir) throws Exception {
// 验证并创建输出目录
if (!Files.exists(outputDir)) {
Files.createDirectories(outputDir);
}
URI uri;
try {
uri = new URI(urlString);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("无效的URL格式");
}
HttpRequest request = HttpRequest.newBuilder(uri)
.GET()
.header("User-Agent", "Java HttpClient Downloader")
.build();
HttpResponse<InputStream> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofInputStream()
);
if (response.statusCode() != 200) {
throw new IOException("服务器返回错误状态码: " + response.statusCode());
}
String fileName = getFileName(uri, response);
Path outputPath = outputDir.resolve(fileName);
System.out.println("开始下载: " + fileName);
System.out.println("保存路径: " + outputPath.toAbsolutePath());
try (InputStream is = response.body()) {
long contentLength = response.headers()
.firstValueAsLong("Content-Length")
.orElse(0L);
if (contentLength > 0) {
downloadWithProgress(is, outputPath, contentLength);
} else {
Files.copy(is, outputPath, StandardCopyOption.REPLACE_EXISTING);
}
}
System.out.println("\n下载完成!");
}
private static String getFileName(URI uri, HttpResponse<?> response) {
// 尝试从Content-Disposition头获取文件名
return response.headers()
.firstValue("Content-Disposition")
.map(header -> {
String[] parts = header.split(";");
for (String part : parts) {
if (part.trim().startsWith("filename=")) {
return part.split("=")[1]
.replace("\"", "")
.trim();
}
}
return null;
})
.orElseGet(() -> {
// 从URL路径获取文件名
String path = uri.getPath();
return path.substring(path.lastIndexOf('/') + 1);
});
}
private static void downloadWithProgress(InputStream is, Path outputPath, long contentLength)
throws IOException {
try (var os = Files.newOutputStream(outputPath)) {
byte[] buffer = new byte[4096];
long bytesRead = 0;
int read;
while ((read = is.read(buffer)) != -1) {
os.write(buffer, 0, read);
bytesRead += read;
// 更新进度显示
double progress = (double) bytesRead / contentLength * 100;
System.out.printf("进度: %.2f%% (%d/%d bytes)\r",
progress, bytesRead, contentLength);
}
}
}
}
功能特点:
- 自动重定向处理 :配置了
followRedirects(HttpClient.Redirect.NORMAL)
- 进度显示:当服务器提供Content-Length时会显示下载进度
- 智能文件名检测 :
- 优先从
Content-Disposition
头获取文件名 - 其次从URL路径解析文件名
- 优先从
- 异常处理:处理常见的网络错误和I/O错误
- 目录自动创建:自动创建不存在的输出目录
- 超时设置:15秒连接超时
使用方式:
bash
java FileDownloader.java <下载URL> [输出目录]
示例:
bash
java FileDownloader.java https://example.com/largefile.zip /tmp/downloads
扩展建议:
- 断点续传 :添加
Range
头支持 - 多线程下载:分割文件范围并行下载
- 速度限制:控制下载带宽
- 代理支持 :通过
.proxy()
方法配置 - HTTPS验证:自定义SSLContext
- 身份验证:添加Authorization头
该实现使用了Java 11+的HttpClient API,具有非阻塞IO特性,适合处理大文件下载。进度显示功能在支持ANSI转义的终端上可以显示动态更新效果。