【问题标题】:How to save the file from HTTPS url in JAVA?如何在 JAVA 中保存来自 HTTPS url 的文件?
【发布时间】:2013-09-05 17:32:40
【问题描述】:

我正在尝试使用输出流从 URL 保存文件。该 URL 受 https 保护。因此,当我尝试按以下方式获取文件时出现了一些错误

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
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
at java.net.URL.openStream(Unknown Source) 
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
    at java.security.cert.CertPathBuilder.build(Unknown Source)
    ... 60 more

假设我想从这个 URL 打开文件

https://www.filepicker.io/api/file/KW9EJhYtS6y48Whm2S6D?signature=4098f262b9dba23e4766ce127353aaf4f37fde0fd726d164d944e031fd862c18&policy=eyJoYW5kbGUiOiJLVzlFSmhZdFM2eTQ4V2htMlM2RCIsImV4cGlyeSI6MTUwODE0MTUwNH0=

所以我做了类似的事情:

try{    
    URL URL = new URL('https://www.filepicker.io/api/file/KW9EJhYtS6y48Whm2S6D?signature=4098f262b9dba23e4766ce127353aaf4f37fde0fd726d164d944e031fd862c18&policy=eyJoYW5kbGUiOiJLVzlFSmhZdFM2eTQ4V2htMlM2RCIsImV4cGlyeSI6MTUwODE0MTUwNH0=');
    String = path = "D://download/";
    InputStream ins = url.openStream();
    OutputStream ous = new FileOutputStream(path);
    final byte[] b = new byte[2048];
    int length;

        while ((length = inputStream.read(b)) != -1) {
               ous.write(b, 0, length);
         }

           ins.close();
           ous.close();
}

由于出现了错误,因此在专用的 floder 中没有发生任何事情。如何从 HTTPS url 获取文件?

【问题讨论】:

    标签: java https httpurlconnection outputstream filepicker.io


    【解决方案1】:

    错误的原因是Java无法确认证书的有效性。这可以通过将证书导入 JAVA 证书存储来修复,或者通过禁用 SSL 证书验证来“修复”。

    【讨论】:

      【解决方案2】:

      HTTPS 连接需要握手。 IE。明确承认对方。服务器已通过 HTTPS 证书标识自己,但您的信任存储中显然没有此证书,并且您的 Java 代码中没有明确承认该标识的地方,所以HttpsURLConnection(在此处的封面下使用) 拒绝继续 HTTPS 请求。

      作为一个启动示例,您可以在您的课程中使用以下代码让HttpsURLConnection 接受所有 SSL 证书,无论您使用什么 HTTPS URL。

      static {
          final TrustManager[] trustAllCertificates = new TrustManager[] {
              new X509TrustManager() {
                  @Override
                  public X509Certificate[] getAcceptedIssuers() {
                      return null; // Not relevant.
                  }
                  @Override
                  public void checkClientTrusted(X509Certificate[] certs, String authType) {
                      // Do nothing. Just allow them all.
                  }
                  @Override
                  public void checkServerTrusted(X509Certificate[] certs, String authType) {
                      // Do nothing. Just allow them all.
                  }
              }
          };
      
          try {
              SSLContext sc = SSLContext.getInstance("SSL");
              sc.init(null, trustAllCertificates, new SecureRandom());
              HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
          } catch (GeneralSecurityException e) {
              throw new ExceptionInInitializerError(e);
          }
      }
      

      如果您希望在每个证书的基础上进行更细粒度的控制,请按照their Javadoc 相应地实施方法。


      与具体问题无关,您的代码中还有第二个问题。您正在尝试将下载的文件保存为文件夹而不是文件。

      String = path = "D://download/";
      OutputStream ous = new FileOutputStream(path);
      

      除了语法错误更可能是在制定问题时粗心造成的(即直接编辑有问题的代码而不是实际复制粘贴工作代码),这没有任何意义。您不应将文件夹指定为保存位置。您应该指定一个文件名。如有必要,您可以从Content-Disposition 标头中提取它,或使用File#createTempFile() 自动生成一个。例如

      File file = File.createTempFile("test-", ".jpg", new File("D:/download/"));
      Files.copy(url.openStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
      

      (如果您已经在使用 Java 7,只需使用 Files#copy() 而不是该样板文件)

      【讨论】:

      • 非常感谢您的回答,@BalusC。我有一个关于 SSL 证书的问题。在这里,您告诉我代码将接受所有 SSL 证书。这意味着它也会信任假证书或未经验证的证书,不是吗?再次感谢您对我的代码中的进一步问题提出的额外建议。
      • 我想询问更多信息,@BalusC 如果我只想信任特定的证书。你能建议我如何做到这一点吗?
      • 在checkServerTrusted() 方法中完成这项工作。如果您不接受证书,请抛出CertificateException。另请参阅此问答:stackoverflow.com/questions/6629473/…
      • 如果你提供了进口会有所帮助
      • 从 Java8u161 开始,我们默认启用了无限加密,因此从 https 下载应该可以工作。更多信息在这里:oracle.com/technetwork/java/javase/8u161-relnotes-4021379.html
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-20
      • 2020-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-10
      • 2021-09-10
      相关资源
      最近更新 更多