java 通过指定url地址下载文件到本地工具类(包含https的数字签名请求)

java 通过指定url地址下载文件到本地工具类(包含https的数字签名请求)
XFileUtils
public class XFileUtils {

    /**
     * 从网络Url中下载文件
     * @param urlStr url的路径
     * @throws IOException
     */
    public static String  downLoadByUrl(String urlStr,String savePath, String fileName) {
        if (StrUtil.isBlank(fileName)) {
            fileName = getFileName(urlStr);
        }
        InputStream inputStream = null;
        FileOutputStream fos = null;
        try {
            URL url = new URL(urlStr);
            // 防止https被证书校验报错,信任所有证书
            if(urlStr.contains("https")){
                SslUtils.ignoreSsl();
            }
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //设置超时间为10秒
            conn.setConnectTimeout(10 * 1000);
            //得到输入流
            inputStream = conn.getInputStream();
            //获取自己数组
            byte[] getData = readInputStream(inputStream);
            //文件保存位置
            File saveDir = new File(savePath);
            if (!saveDir.exists()) { // 没有就创建该文件
                saveDir.mkdir();
            }
            File file = new File(saveDir + File.separator + fileName);
            fos = new FileOutputStream(file);
            fos.write(getData);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(fos);
            IoUtil.close(inputStream);
        }
        return fileName;
    }

    /**
     * 从输入流中获取字节数组
     * @param inputStream
     * @return
     * @throws IOException
     */
    private static  byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[4*1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }

    /**
     * 从src文件路径获取文件名
     * @param srcRealPath src文件路径
     * @return 文件名
     */
    private static String getFileName(String srcRealPath){
        return StringUtils.substringAfterLast(srcRealPath,"/");
    }
}

测试类:

public static void main(String[] args) {
        String url = "https://xxxxxxxxxxxx/kpfw/fpjfzz/v1/exportDzfpwjEwm?Wjgs=PDF&Jym=7D1E&Fphm=23512000000118158368&Czsj=1699335192593&Kprq=20231107111703";
//        String url = "https://www.w3school.com.cn/example/xmle/note.xml";
        String filePath = "D:\\workFile";

        String fileName = XFileUtils.downLoadByUrl(url, filePath, null);
    }
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:371)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:314)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:309)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
	at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:480)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:458)
	at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:201)
	at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
	at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1505)
	at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1420)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426)
	at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:578)
	at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:183)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1665)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589)
	at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:224)
	at example.common.utils.XFileUtils.downLoadByUrl(XFileUtils.java:36)
	at example.common.utils.XFileUtils.main(XFileUtils.java:89)

由上面的测试类,可以发现,http请求是可以正常下载文件到本地,但是如果是https,就会报错。这是因为java对SSL证书不信任造成,需要证书鉴权。这种报错有两种比较常用的解决方式。

解决java对SSL证书不信任造成报错(https)
一、运行或者打包去掉SSL证书检验(不推荐)

-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true

二、导入证书(不推荐)
三、信任所有SSL证书(推荐)
public class SslUtils {

    private static final Logger logger = LoggerFactory.getLogger(SslUtils.class);
    private static void trustAllHttpsCertificates() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[1];
        TrustManager tm = new miTM();
        trustAllCerts[0] = tm;
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }
    static class miTM implements TrustManager, X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public boolean isServerTrusted(X509Certificate[] certs) {
            return true;
        }
        public boolean isClientTrusted(X509Certificate[] certs) {
            return true;
        }
        public void checkServerTrusted(X509Certificate[] certs, String authType)
                throws CertificateException {
            return;
        }
        public void checkClientTrusted(X509Certificate[] certs, String authType)
                throws CertificateException {
            return;
        }
    }
    /**
     * 忽略HTTPS请求的SSL证书,必须在openConnection之前调用
     * @throws Exception
     */
    public static void ignoreSsl() throws Exception{
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                logger.info("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost());
                return true;
            }
        };
        trustAllHttpsCertificates();
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }
}

PS:必须在openConnection之前调用,SslUtils.ignoreSsl();

至此,通过上面的工具类就解决证书不信任,可以通过url地址下载正常下载文件