HttpClient簡介
HttpClient 功能介紹
1. 讀取網頁(HTTP/HTTPS)內容
二、使用POST方式提交數據(httpClient3)
3. 處理頁面重定向
4. 模擬登陸開心網
5. 提交XML格式參數
6. 訪問啓用認證的頁面
7. 多線程模式下使用httpclient
httpClient完整封裝php
HTTP 協議多是如今 Internet 上使用得最多、最重要的協議了,愈來愈多的 Java 應用程序須要直接經過 HTTP 協議來訪問網絡資源。雖然在 JDK 的 java.net 包中已經提供了訪問 HTTP 協議的基本功能,可是對於大部分應用程序來講,JDK 庫自己提供的功能還不夠豐富和靈活。HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,而且它支持 HTTP 協議最新的版本和建議。HttpClient 已經應用在不少的項目中,好比 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient。更多信息請關注http://hc.apache.org/html
如下列出的是 HttpClient 提供的主要的功能,要知道更多詳細的功能能夠參見 HttpClient 的主頁。java
實現了全部 HTTP 的方法(GET,POST,PUT,HEAD 等)apache
支持自動轉向編程
支持 HTTPS 協議瀏覽器
支持代理服務器等安全
應用HttpClient來對付各類頑固的WEB服務器 轉自:http://blog.csdn.net/ambitiontan/archive/2006/01/06/572171.aspx服務器
通常的狀況下咱們都是使用IE或者Navigator瀏覽器來訪問一個WEB服務器,用來瀏覽頁面查看信息或者提交一些數據等等。所訪問的這些頁面有的僅僅是一些普通的頁面,有的須要用戶登陸後方可以使用,或者須要認證以及是一些經過加密方式傳輸,例如HTTPS。目前咱們使用的瀏覽器處理這些狀況都不會構成問題。不過你可能在某些時候須要經過程序來訪問這樣的一些頁面,好比從別人的網頁中「偷」一些數據;利用某些站點提供的頁面來完成某種功能,例如說咱們想知道某個手機號碼的歸屬地而咱們本身又沒有這樣的數據,所以只好藉助其餘公司已有的網站來完成這個功能,這個時候咱們須要向網頁提交手機號碼並從返回的頁面中解析出咱們想要的數據來。若是對方僅僅是一個很簡單的頁面,那咱們的程序會很簡單,本文也就沒有必要大張旗鼓的在這裏浪費口舌。可是考慮到一些服務受權的問題,不少公司提供的頁面每每並非能夠經過一個簡單的URL就能夠訪問的,而必須通過註冊而後登陸後方可以使用提供服務的頁面,這個時候就涉及到COOKIE問題的處理。咱們知道目前流行的動態網頁技術例如ASP、JSP無不是經過COOKIE來處理會話信息的。爲了使咱們的程序能使用別人所提供的服務頁面,就要求程序首先登陸後再訪問服務頁面,這過程就須要自行處理cookie,想一想當你用java.net.HttpURLConnection來完成這些功能時是多麼恐怖的事情啊!何況這僅僅是咱們所說的頑固的WEB服務器中的一個很常見的「頑固」!再有如經過HTTP來上傳文件呢?不須要頭疼,這些問題有了「它」就很容易解決了!cookie
咱們不可能列舉全部可能的頑固,咱們會針對幾種最多見的問題進行處理。固然了,正如前面說到的,若是咱們本身使用java.net.HttpURLConnection來搞定這些問題是很恐怖的事情,所以在開始以前咱們先要介紹一下一個開放源碼的項目,這個項目就是Apache開源組織中的httpclient,它隸屬於Jakarta的commons項目,目前的版本是2.0RC2。commons下原本已經有一個net的子項目,可是又把httpclient單獨提出來,可見http服務器的訪問絕非易事。網絡
Commons-httpclient項目就是專門設計來簡化HTTP客戶端與服務器進行各類通信編程。經過它可讓原來很頭疼的事情如今輕鬆的解決,例如你再也不管是HTTP或者HTTPS的通信方式,告訴它你想使用HTTPS方式,剩下的事情交給httpclient替你完成。本文會針對咱們在編寫HTTP客戶端程序時常常碰到的幾個問題進行分別介紹如何使用httpclient來解決它們,爲了讓讀者更快的熟悉這個項目咱們最開始先給出一個簡單的例子來讀取一個網頁的內容,而後按部就班解決掉前進中的全部問題。
下面是咱們給出的一個簡單的例子用來訪問某個頁面
/** *最簡單的HTTP客戶端,用來演示經過GET或者POST方式訪問某個頁面 *@authorLiudong */ public class SimpleClient { public static void main(String[] args) throws IOException { HttpClient client = new HttpClient(); // 設置代理服務器地址和端口 //client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port); // 使用 GET 方法 ,若是服務器須要經過 HTTPS 鏈接,那隻須要將下面 URL 中的 http 換成 https HttpMethod method=new GetMethod("http://java.sun.com"); //使用POST方法 //HttpMethod method = new PostMethod("http://java.sun.com"); client.executeMethod(method); //打印服務器返回的狀態 System.out.println(method.getStatusLine()); //打印返回的信息 System.out.println(method.getResponseBodyAsString()); //釋放鏈接 method.releaseConnection(); } }
在這個例子中首先建立一個HTTP客戶端(HttpClient)的實例,而後選擇提交的方法是GET或者POST,最後在HttpClient實例上執行提交的方法,最後從所選擇的提交方法中讀取服務器反饋回來的結果。這就是使用HttpClient的基本流程。其實用一行代碼也就能夠搞定整個請求的過程,很是的簡單!
httpclient使用了單獨的一個HttpMethod子類來處理文件的上傳,這個類就是MultipartPostMethod,該類已經封裝了文件上傳的細節,咱們要作的僅僅是告訴它咱們要上傳文件的全路徑便可,下面這裏將給出關於兩種模擬上傳方式的代碼
第一種:模擬上傳url文件(該方式也適合作普通post請求):
/** * 上傳url文件到指定URL * @param fileUrl 上傳圖片url * @param postUrl 上傳路徑及參數,注意有些中文參數須要使用預先編碼 eg : URLEncoder.encode(appName, "UTF-8") * @return * @throws IOException */ public static String doUploadFile(String postUrl) throws IOException { if(StringUtils.isEmpty(postUrl)) return null; String response = ""; PostMethod postMethod = new PostMethod(postUrl); try { HttpClient client = new HttpClient(); client.getHttpConnectionManager().getParams() .setConnectionTimeout(50000);// 設置鏈接時間 int status = client.executeMethod(postMethod); if (status == HttpStatus.SC_OK) { InputStream inputStream = postMethod.getResponseBodyAsStream(); BufferedReader br = new BufferedReader(new InputStreamReader( inputStream)); StringBuffer stringBuffer = new StringBuffer(); String str = ""; while ((str = br.readLine()) != null) { stringBuffer.append(str); } response = stringBuffer.toString(); } else { response = "fail"; } } catch (Exception e) { e.printStackTrace(); } finally { // 釋放鏈接 postMethod.releaseConnection(); } return response; }
第二種:模擬文件上傳到指定位置
/** * 上傳文件到指定URL * @param file * @param url * @return * @throws IOException */ public static String doUploadFile(File file, String url) throws IOException { String response = ""; if (!file.exists()) { return "file not exists"; } PostMethod postMethod = new PostMethod(url); try { //---------------------------------------------- // FilePart:用來上傳文件的類,file即要上傳的文件 FilePart fp = new FilePart("file", file); Part[] parts = { fp }; // 對於MIME類型的請求,httpclient建議全用MulitPartRequestEntity進行包裝 MultipartRequestEntity mre = new MultipartRequestEntity(parts, postMethod.getParams()); postMethod.setRequestEntity(mre); //--------------------------------------------- HttpClient client = new HttpClient(); client.getHttpConnectionManager().getParams() .setConnectionTimeout(50000);// 因爲要上傳的文件可能比較大 , 所以在此設置最大的鏈接超時時間 int status = client.executeMethod(postMethod); if (status == HttpStatus.SC_OK) { InputStream inputStream = postMethod.getResponseBodyAsStream(); BufferedReader br = new BufferedReader(new InputStreamReader( inputStream)); StringBuffer stringBuffer = new StringBuffer(); String str = ""; while ((str = br.readLine()) != null) { stringBuffer.append(str); } response = stringBuffer.toString(); } else { response = "fail"; } } catch (Exception e) { e.printStackTrace(); } finally { // 釋放鏈接 postMethod.releaseConnection(); } return response; }
在JSP/Servlet編程中response.sendRedirect方法就是使用HTTP協議中的重定向機制。它與JSP中的<jsp:forward …>的區別在於後者是在服務器中實現頁面的跳轉,也就是說應用容器加載了所要跳轉的頁面的內容並返回給客戶端;而前者是返回一個狀態碼,這些狀態碼的可能值見下表,而後客戶端讀取須要跳轉到的頁面的URL並從新加載新的頁面。就是這樣一個過程,因此咱們編程的時候就要經過HttpMethod.getStatusCode()方法判斷返回值是否爲下表中的某個值來判斷是否須要跳轉。若是已經確認須要進行頁面跳轉了,那麼能夠經過讀取HTTP頭中的location屬性來獲取新的地址。
下面的代碼片斷演示如何處理頁面的重定向
client.executeMethod(post); System.out.println(post.getStatusLine().toString()); post.releaseConnection(); // 檢查是否重定向 int statuscode = post.getStatusCode(); if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) || (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) || (statuscode ==HttpStatus.SC_SEE_OTHER) || (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) { // 讀取新的 URL 地址 Header header=post.getResponseHeader("location"); if (header!=null){ Stringnewuri=header.getValue(); if((newuri==null)||(newuri.equals(""))) newuri="/"; GetMethodredirect=newGetMethod(newuri); client.executeMethod(redirect); System.out.println("Redirect:"+redirect.getStatusLine().toString()); redirect.releaseConnection(); }else System.out.println("Invalid redirect"); }
咱們能夠自行編寫兩個JSP頁面,其中一個頁面用response.sendRedirect方法重定向到另一個頁面用來測試上面的例子。
本小節應該說是HTTP客戶端編程中最常遇見的問題,不少網站的內容都只是對註冊用戶可見的,這種狀況下就必需要求使用正確的用戶名和口令登陸成功後,方可瀏覽到想要的頁面。由於HTTP協議是無狀態的,也就是鏈接的有效期只限於當前請求,請求內容結束後鏈接就關閉了。在這種狀況下爲了保存用戶的登陸信息必須使用到Cookie機制。以JSP/Servlet爲例,當瀏覽器請求一個JSP或者是Servlet的頁面時,應用服務器會返回一個參數,名爲jsessionid(因不一樣應用服務器而異),值是一個較長的惟一字符串的Cookie,這個字符串值也就是當前訪問該站點的會話標識。瀏覽器在每訪問該站點的其餘頁面時候都要帶上jsessionid這樣的Cookie信息,應用服務器根據讀取這個會話標識來獲取對應的會話信息。
對於須要用戶登陸的網站,通常在用戶登陸成功後會將用戶資料保存在服務器的會話中,這樣當訪問到其餘的頁面時候,應用服務器根據瀏覽器送上的Cookie中讀取當前請求對應的會話標識以得到對應的會話信息,而後就能夠判斷用戶資料是否存在於會話信息中,若是存在則容許訪問頁面,不然跳轉到登陸頁面中要求用戶輸入賬號和口令進行登陸。這就是通常使用JSP開發網站在處理用戶登陸的比較通用的方法。
這樣一來,對於HTTP的客戶端來說,若是要訪問一個受保護的頁面時就必須模擬瀏覽器所作的工做,首先就是請求登陸頁面,而後讀取Cookie值;再次請求登陸頁面並加入登陸頁所需的每一個參數;最後就是請求最終所需的頁面。固然在除第一次請求外其餘的請求都須要附帶上Cookie信息以便服務器能判斷當前請求是否已經經過驗證。說了這麼多,但是若是你使用httpclient的話,你甚至連一行代碼都無需增長,你只須要先傳遞登陸信息執行登陸過程,而後直接訪問想要的頁面,跟訪問一個普通的頁面沒有任何區別,由於類HttpClient已經幫你作了全部該作的事情了,太棒了!下面的例子實現了模擬登錄開心網並向本身好友發送消息的功能。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.httpclient.params.HttpMethodParams; class Login { public static String loginurl = "https://security.kaixin001.com/login/login_post.php"; static Cookie[] cookies = {}; static HttpClient httpClient = new HttpClient(); static String email = "xxx@qq.com";//你的email static String psw = "xxx";//你的密碼 // 消息發送的action String url = "http://www.kaixin001.com/home/"; public static void getUrlContent() throws Exception { HttpClientParams httparams = new HttpClientParams(); httparams.setSoTimeout(30000); httpClient.setParams(httparams); httpClient.getHostConfiguration().setHost("www.kaixin001.com", 80); httpClient.getParams().setParameter( HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8"); PostMethod login = new PostMethod(loginurl); login.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); NameValuePair Email = new NameValuePair("loginemail", email);// 郵箱 NameValuePair password = new NameValuePair("password", psw);// 密碼 // NameValuePair code = new NameValuePair( "code" // ,"????");//有時候須要驗證碼,暫時未解決 NameValuePair[] data = { Email, password }; login.setRequestBody(data); httpClient.executeMethod(login); int statuscode = login.getStatusCode(); System.out.println(statuscode + "-----------"); String result = login.getResponseBodyAsString(); System.out.println(result+"++++++++++++"); cookies = httpClient.getState().getCookies(); System.out.println("==========Cookies============"); int i = 0; for (Cookie c : cookies) { System.out.println(++i + ": " + c); } httpClient.getState().addCookies(cookies); // 當state爲301或者302說明登錄頁面跳轉了,登錄成功了 if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) || (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) || (statuscode == HttpStatus.SC_SEE_OTHER) || (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) { // 讀取新的 URL 地址 Header header = login.getResponseHeader("location"); // 釋放鏈接 login.releaseConnection(); System.out.println("獲取到跳轉header>>>" + header); if (header != null) { String newuri = header.getValue(); if ((newuri == null) || (newuri.equals(""))) newuri = "/"; GetMethod redirect = new GetMethod(newuri); // //////////// redirect.setRequestHeader("Cookie", cookies.toString()); httpClient.executeMethod(redirect); System.out.println("Redirect:" + redirect.getStatusLine().toString()); redirect.releaseConnection(); } else System.out.println("Invalid redirect"); } else { // 用戶名和密碼沒有被提交,當登錄屢次後須要驗證碼的時候會出現這種未提交狀況 System.out.println("用戶沒登錄"); System.exit(1); } } public static void sendMsg() throws Exception { // 登陸後發消息 System.out.println("*************發消息***********"); String posturl = "http://www.kaixin001.com/msg/post.php"; PostMethod poster = new PostMethod(posturl); poster.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); poster.setRequestHeader("Cookie", cookies.toString()); NameValuePair uids = new NameValuePair("uids", "89600585");// 發送的好友對象的id,此處換成你的好友id NameValuePair content = new NameValuePair("content", "你好啊!");// 須要發送的信息的內容 NameValuePair liteeditor_0 = new NameValuePair("liteeditor_0", "你好啊!");// 須要發送的信息的內容 NameValuePair texttype = new NameValuePair("texttype", "plain"); NameValuePair send_separate = new NameValuePair("send_separate", "