You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java NIO下载Windows正常macOS返回403 Forbidden问题排查

问题现象

编写的批量从服务器重新下载文件的小型命令行程序,在Windows系统下运行无异常,在macOS系统下无法正常工作,运行时抛出如下403报错:

Download failed: java.io.IOException: Server returned HTTP response code: 403 for URL:
https://cdn.modrinth.com/data/P7dR8mSH/versions/0.56.3+1.19/fabric-api-0.56.3%2B1.19.jar
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1914)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1512)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:268)
    at java.net.URL.openStream(URL.java:1092)
    at main.downloadUsingNIO(main.java:37)
    at main.main(main.java:20)

原始实现代码

private static void downloadUsingNIO(String urlStr, String file) throws IOException {
    URL url = new URL(urlStr);
    ReadableByteChannel rbc = Channels.newChannel(url.openStream());
    FileOutputStream fos = new FileOutputStream(file);
    fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
    fos.close();
    rbc.close();
}

调用方式:

downloadUsingNIO(URLOFFILE, WHERETOSAVEANDTOWHATFILE);

已排查确认的信息

  • Windows与macOS端运行程序的账户均为系统主管理员账户
  • 执行sudo java -jar 命令提权运行jar包无法解决问题
  • 同一下载链接在Safari浏览器中可正常下载文件
  • 执行curl -o thing.jar 命令可正常完成下载
  • 曾尝试使用Apache Commons.IO实现下载逻辑,因底层调用逻辑与原实现一致未生效,报错栈完全相同
根因分析

报错核心原因是目标CDN拦截了Java默认请求的User-Agent标识。
Java原生URL.openStream()发起HTTP请求时,默认携带的User-Agent会携带Java版本特征,和浏览器、curl等常规客户端的UA特征差异明显,触发了Modrinth CDN的拦截规则返回403;Safari、curl的默认UA在CDN的放行规则内,因此可以正常下载。不同系统下安装的JDK版本默认UA存在细微差异,导致Windows端未触发拦截、macOS端触发拦截。

修复方案

手动为请求配置合规的浏览器User-Agent即可解决问题,修复后的代码同时补充了本地文件完整性校验、存储目录自动创建的逻辑,健壮性更强:

private static void downloadUsingNIO(String Strurl, String path) throws IOException {

    final URLConnection connection = new URL(Strurl).openConnection();
    connection.addRequestProperty("User-Agent",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0");

    final int contentLength = connection.getContentLength();
    final File end = new File(path);

    if (end.exists()) {
        final URLConnection savedFileConnection = end.toURI().toURL().openConnection();
        if (savedFileConnection.getContentLength() == contentLength) {
            return;
        }
    } else {
        final File dir = end.getParentFile();
        if (!dir.exists())
            dir.mkdirs();
    }

    final ReadableByteChannel rbc = Channels.newChannel(connection.getInputStream());
    final FileOutputStream fos = new FileOutputStream(end);
    fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
    fos.close();
    
    return;
}

内容的提问来源于stack exchange,提问作者NoThrottle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 21:30:47