使用Java的HttpClient实现文件下载器

使用Java的HttpClient实现文件下载器

下载器通常需要从指定的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实现的文件下载器示例,包含进度显示和基础错误处理:

代码语言:javascript复制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 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秒连接超时使用方式:代码语言:javascript复制java FileDownloader.java <下载URL> [输出目录]示例:

代码语言:javascript复制java FileDownloader.java https://example.com/largefile.zip /tmp/downloads扩展建议:断点续传:添加Range头支持多线程下载:分割文件范围并行下载速度限制:控制下载带宽代理支持:通过.proxy()方法配置HTTPS验证:自定义SSLContext身份验证:添加Authorization头该实现使用了Java 11+的HttpClient API,具有非阻塞IO特性,适合处理大文件下载。进度显示功能在支持ANSI转义的终端上可以显示动态更新效果。

相关作品

100千焦等于多少热卡路里 365bet娱乐投注

100千焦等于多少热卡路里

❤️ 541 📅 08-19
mac气垫怎么样?mac气垫bb好用吗? 365bet特点

mac气垫怎么样?mac气垫bb好用吗?

❤️ 643 📅 08-22
小米音箱报价 365bet特点

小米音箱报价

❤️ 96 📅 09-14