如何使用java语言下载https的网络文件

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;

public class HttpsDownloader {
    public static void main(String[] args) {
        String fileURL = "https://example.com/file.txt"; // 要下载的文件URL
        String savePath = "downloaded_file.txt"; // 下载后保存的文件路径

        try {
            URL url = new URL(fileURL);
            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            connection.connect();

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpsURLConnection.HTTP_OK) {
                InputStream inputStream = new BufferedInputStream(connection.getInputStream());
                FileOutputStream outputStream = new FileOutputStream(savePath);

                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }

                outputStream.close();
                inputStream.close();
                System.out.println("文件下载完成!");
            } else {
                System.out.println("无法连接到文件URL。响应代码:" + responseCode);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上述代码首先创建一个URL对象,然后使用openConnection()方法获取到HttpsURLConnection对象。接下来,通过调用connect()方法建立与服务器的连接。

如果连接成功(HTTP响应码为200),则可以从输入流中读取数据,并将其写入指定的文件输出流中。最后,关闭输入流和输出流,并打印出下载完成的消息。

请将fileURL替换为您要下载的文件的真实URL,将savePath替换为下载文件的保存路径。运行代码后,您将在指定位置找到下载的文件。

上面代码报错,javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

这个错误通常发生在尝试通过HTTPS下载文件时,因为服务器的证书无法验证或未被信任。解决方法是将服务器的证书添加到Java的信任存储库中。以下是一种简单的方法:

  1. 首先,从浏览器中导出服务器的证书(例如Chrome浏览器):

    • 在浏览器中打开目标URL。
    • 单击地址栏旁边的锁图标,并选择"证书"或"证书详细信息"。
    • 在证书详细信息窗口中,导航到"详细信息"选项卡。
    • 单击"复制到文件"按钮,并按照指示保存证书,选择DER格式。
  2. 使用keytool工具将证书添加到Java的信任存储库:

    • 打开命令提示符或终端。

    • 导航到Java的安装目录的bin目录下(例如:C:\Program Files\Java\jdk1.8.0_271\bin)。

    • 运行以下命令,将证书添加到信任存储库中(替换path/to/certificate.der为导出的证书路径和名称):

keytool -importcert -alias mycert -keystore cacerts -file path/to/certificate.der

您将被要求输入Java信任存储库的密码,默认情况下是changeit

3.运行代码时,Java将能够验证服务器的证书并建立安全的HTTPS连接。

如果您仍然遇到问题,请确保您的Java运行时环境是最新版本,并尝试使用更可靠的证书进行测试。

如果不添加证书,如何绕过证书?

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class HttpsDownloader {
    public static void main(String[] args) {
        String fileURL = "https://example.com/file.txt"; // 要下载的文件URL
        String savePath = "downloaded_file.txt"; // 下载后保存的文件路径

        try {
            // 创建信任所有证书的 TrustManager
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }

                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                        }

                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                        }
                    }
            };

            // 设置 SSL 上下文
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

            URL url = new URL(fileURL);
            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            connection.connect();

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpsURLConnection.HTTP_OK) {
                InputStream inputStream = new BufferedInputStream(connection.getInputStream());
                FileOutputStream outputStream = new FileOutputStream(savePath);

                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }

                outputStream.close();
                inputStream.close();
                System.out.println("文件下载完成!");
            } else {
                System.out.println("无法连接到文件URL。响应代码:" + responseCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这段代码创建了一个自定义的TrustManager,其中实现了checkClientTrusted()checkServerTrusted()方法,并且不会抛出异常或进行任何验证。然后,通过调用SSLContext.getInstance("TLS")得到一个SSLContext实例,并使用自定义的TrustManager初始化它。最后,调用HttpsURLConnection.setDefaultSSLSocketFactory()将自定义的SSL上下文设置为默认的SSLSocketFactory。

请注意,这种绕过证书验证的方法会降低安全性,因为它允许信任所有证书,包括不受信任或伪造的证书。仅在测试环境中使用此方法,并确保在生产环境中使用有效且受信任的证书来确保安全性。

上面的代码提示:javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present

如果您在绕过证书验证时遇到javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present错误,这表示服务器的证书中缺少主体备用名称(Subject Alternative Names)。

为了解决这个问题,您可以使用一个自定义的HostnameVerifier来跳过主体备用名称的验证。以下是修改后的示例代码:

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

public class HttpsDownloader {
    public static void main(String[] args) {
        String fileURL = "https://example.com/file.txt"; // 要下载的文件URL
        String savePath = "downloaded_file.txt"; // 下载后保存的文件路径

        try {
            // 创建信任所有证书的 TrustManager
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }

                        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                        }

                        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                        }
                    }
            };

            // 设置 SSL 上下文
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

            // 创建自定义 HostnameVerifier
            HostnameVerifier allHostsValid = new HostnameVerifier() {
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

            URL url = new URL(fileURL);
            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            connection.connect();

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpsURLConnection.HTTP_OK) {
                InputStream inputStream = new BufferedInputStream(connection.getInputStream());
                FileOutputStream outputStream = new FileOutputStream(savePath);

                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }

                outputStream.close();
                inputStream.close();
                System.out.println("文件下载完成!");
            } else {
                System.out.println("无法连接到文件URL。响应代码:" + responseCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

以上代码除了添加自定义的HostnameVerifier外,其他部分与之前的代码相同。使用这个自定义的HostnameVerifier,将绕过对主体备用名称的验证,并允许连接到没有主体备用名称的服务器。

请注意,绕过主体备用名称的验证也会降低安全性,因为它可能容易受到中间人攻击。仅在测试环境中使用此方法,并在生产环境中使用正确配置的证书和合适的验证机制以确保安全性。