登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  java教程

Java HttpClient ofFileDownload 怎样保存响应文件名

来源:17golang原创

时间:2026-10-17 13:02:16 365浏览 收藏

Java HttpClient 的 BodyHandlers.ofFileDownload 接收的是保存目录,文件名取自响应头 Content-Disposition,而不是下载地址最后一段。用于受控附件接口时,先准备目录,显式指定 WRITE 和 CREATE_NEW,再把返回的 Path 交给业务层;不要让重复下载静默覆盖旧文件。

先固定响应文件名契约

本文按 Java 26 API 说明编写,适用于服务端可协商附件响应头的场景。Content-Disposition 需要 attachment 类型以及 filename 参数,例如下面的契约示例。这不是一次真实下载的抓包结果。

Content-Disposition: attachment; filename="report.csv"

请求地址可以是 /exports/current,保存名称仍然由 filename 决定。如果上游只给 filename*,不要假定它与这里要求的 filename 完全等价;应先让接口提供兼容的 filename 参数。也不要把 Content-Type 当作文件名来源。

图中区分的是命名责任,不是网络执行顺序。

响应文件名与本地保存目录的责任关系
图1:URL、响应文件名和本地目录分别承担不同责任。

目录和重名策略由客户端决定

目录要先建好,再创建处理器。示例使用当前工作目录下的 downloads,仅作为部署示例;生产环境应由可信配置指定专用目录,不接受请求参数任意指定路径。目录需属于默认文件系统且可写。启动时可写,不代表请求期间权限不会变化,因此仍须处理写入失败。

这里选 CREATE_NEW:同名目标已经存在时让操作失败,不先删除旧文件,也不做“先判断不存在再创建”的竞态检查。需要覆盖的系统必须另行定义保留规则,不能简单把 CREATE_NEW 换成 CREATE 就以为会截断旧文件。本文不实现覆盖策略。

把状态码检查放在写入之前

以下示例类接收已构建的 HttpClient 和 HttpRequest,不替调用方选择远程地址、凭据或重试策略。只接受 200 响应;其他状态的响应体直接丢弃,最终抛出业务可见的 IOException,避免将错误页保存成附件。若你的接口使用其他成功状态,应单独确认其语义。

import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public final class AttachmentDownload {
    private AttachmentDownload() {}

    public static Path save(HttpClient client, HttpRequest request,
                            Path directory)
            throws IOException, InterruptedException {
        // 目录来自可信配置;在创建文件处理器之前准备好目录。
        Path target = Files.createDirectories(directory.toAbsolutePath().normalize());

        // 使用新建语义,存在同名文件时失败,不覆盖已有附件。
        HttpResponse.BodyHandler attachment =
                HttpResponse.BodyHandlers.ofFileDownload(
                        target,
                        StandardOpenOption.WRITE,
                        StandardOpenOption.CREATE_NEW);

        // 非200响应不使用文件订阅器,避免错误正文进入下载目录。
        HttpResponse.BodyHandler guarded = info -> {
            if (info.statusCode() == 200) {
                return attachment.apply(info);
            }
            return HttpResponse.BodySubscribers.replacing((Path) null);
        };

        HttpResponse response = client.send(request, guarded);
        if (response.statusCode() != 200) {
            throw new IOException("下载响应状态异常: " + response.statusCode());
        }

        // 正常返回的Path表示正文已写入,后续仍由业务层判断内容是否可用。
        return response.body();
    }
}

该示例没有在本文环境发出下载请求,也没有声明经过编译或接口测试。调用端应配置请求超时;捕获 InterruptedException 后若不继续向上抛出,应恢复线程中断标记。不要把超时或中断包装成一个看似成功的保存路径。

失败路径不要伪装成默认文件名

缺少符合要求的响应头时,应反馈接口契约错误,而不是悄悄改为 download.bin;这样才能区分服务端响应变化和本地磁盘问题。具体异常传播应保留 cause,记录状态码、目标目录和必要的响应头诊断信息,但不要记录授权头、Cookie 或完整敏感地址。

本地目录不满足条件,可能在创建处理器时就以 IllegalArgumentException 暴露;请求期间的写入失败则需要按 I/O 失败处理。创建了处理器不代表已经成功建立文件。网络中途失败也不能假定没有残留文件;清理只能针对本次任务确认为自己创建的路径,不能按猜测的响应文件名批量删除目录内容。

目录权限重名策略和完成语义的边界
图2:文件写入完成与业务验收是两件事。

上线前明确的几个边界

响应带多级路径名称时,该API仅采用最后一个路径组件,但这不等于附件内容可信、后缀可信或下载目录可以共享给不可信本地用户。生产目录应限制权限,不把下载目录直接当作可执行内容目录。

验收清单应至少覆盖:正常 attachment 与 filename、缺失 filename、错误状态码、同名目标已存在、目录不可写、传输中断。这里列出的是建议用例,不是已完成的测试记录。若必须强制由客户端命名文件,应采用指定完整目标路径的 ofFile,而不是继续让 ofFileDownload 承担另一种命名需求。

最终交付接口应返回真实落盘 Path,并由业务层单独判断内容格式、预期类型与后续可用性。本文仅解决响应文件名落盘,不扩展为通用下载器、断点续传或自动重试器。

官方入口与参考

可复制的Java API官方入口:https://docs.oracle.com/en/java/javase/26/docs/api/java.net.http/java/net/http/HttpResponse.BodyHandlers.html

参考:Oracle Java 26 BodyHandlers;Oracle Java 26 BodySubscribers。文件名与目录条件按前者说明,非成功响应丢弃正文的包装方式为本文示例设计。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>