簡體   English   中英

zip文件下載可與apache httpclient v4.3.6一起使用,但無法用於其他版本

[英]A zip file download works with apache httpclient v4.3.6 but fails for other version

這有點奇怪,我已經進行了足夠的研究,找到了造成此問題的原因和解決方案。 我的目標是從也需要登錄的安全URL下載一個zip文件。 當我使用版本4.3.6的apache httpClient maven依賴項時,一切工作正常。 但是,由於我的aws-sdk-java-core maven依賴項也具有httpclient依賴項,因此無法使用此版本,並且使用v4.3.6會使aws-sdk-java抱怨NoSuchMethod運行時異常。 我了解這個問題。 原因是apache httpclient v4.3.6依賴關系在maven依賴關系樹中比aws-sdk-java-core依賴關系使用的版本(4.5.1)更近。 無論如何,我將減少更多細節,因為我很確定我應該使所有工作都與一個Maven依賴項版本一起使用,而不要使用同一jar的多個版本。 回到原始問題。 由於無法使用v4.3.6,我告訴我的代碼使用v4.5.1,這就是當文件下載代碼開始出現問題時。 當我使用httpclient v4.5.1時,響應會給我以下html內容,而不是給我在請求的https url上擁有的zip文件。

<html>
<HEAD><META HTTP-EQUIV='PRAGMA' CONTENT='NO-CACHE'><META HTTP-EQUIV='CACHE-    
CONTROL' CONTENT='NO-CACHE'>
<TITLE>SAML 2.0 Auto-POST form</TITLE>
</HEAD>
<body onLoad="document.forms[0].submit()">
<NOSCRIPT>Your browser does not support JavaScript.  Please click the    
'Continue' button below to proceed. <br><br>
</NOSCRIPT>
<form action="https://githubext.deere.com/saml/consume" method="POST">
<input type="hidden" name="SAMLResponse"  value="PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERl">
<input type="hidden" name="RelayState" value="2F1HpzrUy5FdX">
<NOSCRIPT><INPUT TYPE="SUBMIT" VALUE="Continue"></NOSCRIPT>
</form>
</body>
</html>

當我使用v4.3.6時,響應將給我zip文件作為預期響應。 我嘗試通過添加更多代碼來手動提交此html內容,但響應保持不變。 下面提供了我用於文件下載的原始代碼。

@Component
public class FileDAO {

    public static void main(String args[]) throws Exception{
        new FileDAO().loadFile("https://some_url.domain.com/zipball/master","myfile.zip");
    }


    public String loadFile(String url, String fileName) throws ClientProtocolException, IOException {

        HttpClient client = login();
        HttpResponse response = client.execute(new HttpGet(url));
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            String unzipToFolderName = fileName.replace(".", "_");
            FileOutputStream outputStream = new FileOutputStream(new File(fileName));
            writeToFile(outputStream, response.getEntity().getContent());            
            return unzipToFolderName;
        } else {
            throw new RuntimeException("error downloading file, HTTP Status code: " + statusCode);
        }
    }

    private void writeToFile(FileOutputStream outputStream, InputStream inputStream)  {
        try {
            int read = 0;
            byte[] bytes = new byte[1024];
            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
        } catch (Exception ex) {
            throw new RuntimeException("error writing zip file, error message : " + ex.getMessage(), ex);
        } finally {
            try {
                outputStream.close();
                inputStream.close();
            } catch (Exception ex) {}
        }
    }

    private HttpClient login() throws IOException {
        HttpClient client = getHttpClient();

        HttpResponse response = client.execute(new HttpGet("https://some_url.domain.com"));
        String responseBody = EntityUtils.toString(response.getEntity());
        Document doc = Jsoup.parse(responseBody);
        org.jsoup.select.Elements inputs = doc.getElementsByTag("input");
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            HttpPost httpPost = new HttpPost("https://some_url.domain.com/saml/consume");
            List<NameValuePair> data = new ArrayList<NameValuePair>();
            data.add(new BasicNameValuePair("SAMLResponse", doc.select("input[name=SAMLResponse]").val()));
            data.add(new BasicNameValuePair("RelayState", doc.select("input[name=RelayState]").val()));
            httpPost.setEntity(new UrlEncodedFormEntity(data));
            HttpResponse logingResponse = client.execute(httpPost);
            int loginStatusCode = logingResponse.getStatusLine().getStatusCode();
            if (loginStatusCode != 302) {
                throw new RuntimeException("clone repo dao. error during login, HTTP Status code: " + loginStatusCode);
            }
        }
        return client;
    }

    private HttpClient getHttpClient() {
        CredentialsProvider provider = new BasicCredentialsProvider();
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("userId", "password");
        provider.setCredentials(AuthScope.ANY, credentials);
        return HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build();
    }
}

我仍在分析4.3.6以外的Apache httpclient版本出了什么問題。 相同的代碼適用於4.3.6,但不適用於高於4.3.6的版本。 任何幫助都非常感謝。 謝謝你們。

問題解決了。 在經過apache httpclient文檔並認真調試日志之后,我可以解決此問題。 我必須創建兩個服務器日志,一個用於v4.3.6,另一個用於v4.5.2。 我開始比較服務器日志,發現罪魁禍首是cookie類型。 舊版本的Cookie類型已(自動)配置為BEST_MATCH,並且可以正常工作。 但是,對於v4.5.2,已從Apache中棄用了BEST_MATCH cookie類型。 添加更多代碼后,我一直在嘗試使用cookie設置,但是服務器響應發送的cookie與我在客戶端代碼中配置的DEFAULT cookie類型不匹配。 這導致cookie無法正確設置,這就是為什么響應返回SAML響應(再次登錄頁面)而不是zip文件的原因。

Apache cookie規范針對cookie規范說明了這一點:

默認值 :默認cookie策略是一種綜合策略,它基於隨HTTP響應發送的cookie的屬性(例如,版本屬性,現在已過時),采用RFC 2965,RFC 2109或Netscape草案兼容的實現。 在下一個次要版本的HttpClient中,將不推薦使用此策略,而推薦使用標准(兼容RFC 6265)。 嚴格標准 :符合RFC 6265第4節定義的行為規范的語法和語義的狀態管理策略。

我將cookie配置更新為STANDARD_STRICT模式,並且一切都開始在最新版本4.5.2下工作。

這是更新的getHttpClient()方法:

private CloseableHttpClient getHttpClient() {
    CredentialsProvider provider = new BasicCredentialsProvider();
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(gitUserId, gitPassword);
    provider.setCredentials(AuthScope.ANY, credentials);
    RequestConfig config = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
    return HttpClientBuilder.create().setDefaultCredentialsProvider(provider).setDefaultRequestConfig(config).setRedirectStrategy(new LaxRedirectStrategy()).build();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM