https://www.cnblogs.com/shijiaqi1066/p/3753224.html
1. URLConnection概述
URLConnection是一個抽象類,表示指向URL指定資源的活動連接。
URLConnection類本身依賴於Socket類實現網絡連接。一般認為,URLConnection類提供了比Socket類更易於使用、更高級的網絡連接抽象。但實際上,大多數程序員都會忽略它。因為URLConnection太貼近HTTP協議。它假定傳輸的內容前面都有MIME首部或類似的東西。但大多數協議(如FTP和SMTP)並不使用MIME首部。
URLConnection繼承樹查看如下:
URLConnection類聲明為抽象類,除了connect()方法,其他方法都已經實現。URLConnection的三個實現子類都位於sun.net.www包下。
URLConnection類的保護類型的構造方法:
protected URLConnection(URL url)
構造一個到指定 URL 的 URL 連接。
connect()方法由子類實現本地與服務器的連接方式。一般使用TCP socket,但也可以使用其他其他機制來建立。
abstract void connect()
打開到此 URL 引用的資源的通信鏈接(如果尚未建立這樣的連接)。
當派生URLConnection子類時,通常會覆蓋URLConnection的其他方法,使其返回有意義的值。
URLConnection類聲明為抽象類,除了connect()方法,其他方法都已經實現。URLConnection的三個實現子類都位於sun.net.www包下。
1.1 讀取首部
String getContentType() 返回 Content-type 頭字段的值。即數據的MIME內容類型。若類型不可用,則返回null。 除了HTTP協議,極少協議會使用MIME首部。若內容類型是文本。則Content-type首部可能會包含一個標識內容編碼方式的字符集。 例:Content-type:text/html; charset=UTF-8 int getContentLength() 返回 Content-length 頭字段的值。該方法獲取內容的字節數。許多服務器只有在傳輸二進制文件才發送Content-length首部,在傳輸文本文件時並不發送。若沒有該首部,則返回-1。 若需要知道讀取的具體字節數,或需要預先知道創建足夠大的緩沖區來保存數據時,可以使用該方法。 String getContentEncoding() 返回 Content-encoding 頭字段的值。獲取內容的編碼方式。若內容無編碼,則該方法返回null。 注意:Content-encoding(內容編碼)與字符編碼不同。字符編碼方式由Content-type首部或穩定內容的信息確定,它指出如何使用字節指定字符。內容編碼方式則指出字節如何編碼其他字節。 例:若編碼格式為x-gzip,則可以使用java.util.zip.GZipInputStream來解碼。 long getDate() 返回 date 頭字段的值。獲取請求的發送時間。即自1900年1月1日子夜后過去的毫秒數。 注意:這是從服務器的角度看到的發送時間。可能與本地時間不一致。若首部不包括Date字段,則返回0。 long getExpiration() 返回 expires 頭字段的值。獲取Expires的值。若無該字段,則返回0。0即表示不過期,永遠緩存。 注意:該值是自1970年1月1日上午12:00后的毫秒數。 long getLastModified() 返回 last-modified 頭字段的值。該值是自1900年1月1日子夜后過去的毫秒數。若無該字段,則返回0。
1.2 獲取任意首部字段
String getHeaderField(String name) 返回指定的頭字段的值。 Map<String,List<String>> getHeaderFields() 返回頭字段的不可修改的 Map。 String getHeaderFieldKey(int n) 返回第 n 個頭字段的鍵。 String getHeaderField(int n) 返回第 n 個頭字段的值。 long getHeaderFieldDate(String name, long Default) 返回解析為日期的指定字段的值。 int getHeaderFieldInt(String name, int Default) 返回解析為數字的指定字段的值。
1.3 配置連接
URLConnection類的保護類型字段如下: protected URL url 該字段指定URLConnection連接的URL。構造函數會在創建URLConnection時設置,此后就不能再改變。 protected boolean connected 當連接打開時,該字段為true。若連接關閉,則為false。沒有直接讀取或改變connected值的方法。任何導致URLConnection連接的方法都會將此變量設為true,包括connect(),getInputStream(),和getOutputStream()。 任何導致URLConnection斷開的方法都會將此字段設為false。 java.net.URLConnection沒有斷開方法,但一些子類,如java.net.HttpURLConnection中存在這樣的方法。 protected boolean allowUserInteraction 該字段表示是否允許與用戶交互。默認為false,即不允許交互。該值只能在URLConnection連接前設置。 例:Web瀏覽器可能需要訪問用戶名和口令。 protected boolean doInput 該字段在URLConnection可用於輸入時為true,否則為false。默認為true。 protected boolean doOutput 該字段在URLConnection可用於輸出時為true,否則為false。默認為false。 protected long ifModifiedSince 該字段指示了將放置If-Modified-Since首部字段中的日期(格林威治標准時間1970年1月1日子夜后的毫秒數)。 protected boolean useCaches 該字段指定了是否可以在緩存可用時使用緩存。默認為true,表示緩存將被使用;false表示緩存不被使用。
以上字段定義了客戶端如何向服務器作出請求。這些字段只能在URLConnection連接前修改。否則拋出IllegalStateException異常。
每個字段都有一些相應的方法。
以上字段定義了客戶端如何向服務器作出請求。這些字段只能在URLConnection連接前修改。否則拋出IllegalStateException異常。
每個字段都有一些相應的方法。
- URL getURL()
- void setDoInput(boolean doinput)
- boolean getDoInput()
- void setDoOutput(boolean dooutput)
- boolean getDoOutput()
- void setUseCaches(boolean usecaches)
- boolean getUseCaches()
- void setAllowUserInteraction(boolean allowuserinteraction)
- boolean getAllowUserInteraction()
- void setIfModifiedSince(long ifmodifiedsince)
- long getIfModifiedSince()
還有一些方法,這些方法用於定義和獲取URLConnection對象的默認行為:
- static boolean getDefaultAllowUserInteraction()
- boolean getDefaultUseCaches()
- void setDefaultUseCaches(boolean defaultusecaches)
- static void setDefaultAllowUserInteraction(boolean defaultallowuserinteraction)
- static void setFileNameMap(FileNameMap map)
- static FileNameMap getFileNameMap()
1.4 關於超時
Java 1.5增加了如下4個方法,允許查詢和修改連接的超時值。即底層socket在拋出SocketTimeoutException異常前等待遠端響應時間。
設置獲取讀取的超時值,單位為毫秒。
int getReadTimeout()
void setReadTimeout(int timeout)
設置獲取連接的超時值,單位為毫秒。
int getConnectTimeout()
void setConnectTimeout(int timeout)
這四個方法將0解釋為永不超時。如果超時值為負數。兩個設置方法都會拋出IllegalArgumentException異常。
1.5 配置HTTP首部
客戶端向服務器發送請求,不僅會發送請求行,還發送首部。Web服務器可以根據此信息向不同客戶端提供不同的頁面,獲取和設置cookie,通過口令設置用戶等等。通過在客戶端發送和服務器響應的首部中放置不同的字段,就可以完成這些工作。
每個URLConnection的具體子類都在首部中設置一些默認的鍵值對(實際上只有HttpURLConnection這樣做,因為HTTP是唯一以這種方式使用首部的主要協議。)
通過以下方法可以設置URLConnection在打開連接增加HTTP首部:
void setRequestProperty(String key, String value)
此方法只在連接打開之前打開,將會拋出IllegalStateException異常。但該方法僅支持對一個首部設置一個值。
HTTP允許一個熟悉有多個值。各個值之間用逗號隔開。
例:cookie:username=sjq; session=hdfu23asdjf901j;
使用如下方法為首部增加值:
void addRequestProperty(String key, String value)
該方法用於添加某個指定首部字段的值。
使用如下方法獲取首部字段:
String getRequestProperty(String key)
Map<String,List<String>> getRequestProperties()
以上方法用於獲取URLConneciton所擁有HTTP首部。
1.6 使用URLConnection與服務器交互
InputStream getInputStream()
返回從此打開的連接讀取的輸入流。
OutputStream getOutputStream()
返回寫入到此連接的輸出流。
若只是向服務器請求數據,則為HTTP請求方法為GET。
若需要向服務器提交數據,必須在先調用setDoOutput(true)。當doOutput屬性為true時,請求方法將由GET變為POST。
@Test public void test(){ try { URL url = new URL("http://www.baidu.com"); URLConnection uc = url.openConnection(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
例:使用URLConnection請求Baidu首頁。
該程序請求方法為get。使用getInputStream()方法輸出的內容只有響應體,不包含響應的首部。
@Test public void testGetInputStream(){ InputStream in = null; try { //連接 URL url = new URL("http://www.baidu.com"); URLConnection uc = url.openConnection(); //獲取讀入流 in = uc.getInputStream(); //放入緩存流 InputStream raw = new BufferedInputStream(in); //最后使用Reader接收 Reader r = new InputStreamReader(raw); //打印輸出 int c; while((c = r.read())>0){ System.out.print((char)c); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ try { if(in!=null){ in.close(); } } catch (IOException e) { e.printStackTrace(); } } }
1.6 獲得內容
Object getContent()
該方法實質上與URL類的getContent()方法相同。使用該方法JVM需要識別和理解該內容類型。該方法只用於支持類似HTTP的協議,協議需要對MIME內容類型有較清楚的了解。若內容未知,或協議無法理解內容類型,getContent()會拋出一個UnknowServiceException異常。
Object getContent(Class[] classes)
該方法可以選擇將內容轉換為哪個類對象,以提供數據的不同對象表示。該方法嘗試以classes數組中某個類的形式返回內容。優先順序就是數組的順序。若任何一種請求類型都不可用,此方法會返回null。
1.7 ContentHandlerFactory
URLConnection類包括一個靜態的ContentHandler。即內容處理器。通過以下方法設置內容處理器工廠。
static void setContentHandlerFactory(ContentHandlerFactory fac)
1.8 URLConnection的安全
建立網絡連接、讀寫文件等等存在一些安全限制,URLConnection對象會受到這些安全限制的約束。
Permission getPermission()
該方法返回Permission對象,指出連接此URL所需的許可權。如果沒有所需的許可權(即沒有適當的安全管理器),它就會返回null。
1.9 猜測MIME內容類型
static String guessContentTypeFromName(String fname)
根據 URL 的指定 "file" 部分嘗試確定對象的內容類型。
static String guessContentTypeFromStream(InputStream is)
根據輸入流的開始字符嘗試確定輸入流的類型。
2. HttpURLConnection
java.net.HttpURLConnection類是URLConnection的抽象子類;它提供了一些有助於專門操作http URL的額外方法。
sun.net.www.protocol.http包中也提供了一個HttpURLConnection類。該類實現了抽象的connect()方法。但一般很少直接訪問該類。
2.1 獲取HttpURLConnection對象
@Test void testHttpURLConnection(){ try { URL url = new URL("http:www.baidu.com"); URLConnection urlConnection = url.openConnection(); HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
2.2 請求方法
HTTP協議提供了7中請求方法:
- GET
- POST
- HEAD
- PUT
- OPTIONS
- DELETE
- TRACE
通過以下方法設置請求方法:
void setRequestMethod(String method)
若請求不是7個方法的字符串,則會拋出java.net.ProtocolException異常。一般只設置請求方法是不夠的,還需要調整HTTP首部,還要提供消息體。
以下方法獲取HTTP請求方法
String getRequestMethod()
2.3 斷開與服務器的連接
HTTP的Keep-Alive對Web連接的性能有所提升,允許多個請求和響應通過一個TCP連接連續發送。
客戶端通過HTTP請求首部包含Connection字段及Keep-Alive,表示客戶端願意使用Keep-Alive:
Connection: Keep-Alive
但當使用Keep-Alive時,服務器不再因為向客戶端發送最后一個字節數據就關閉連接。畢竟客戶端還可能發送另一個請求。因此客戶端需要復雜關閉連接。
Java提供如下方法關閉連接,實際上很少使用該方法:
abstract void disconnect()
2.4 獲取請求
如果HTTP響應狀態為4xx(客戶端錯誤)或者5xx(服務器錯誤),你可以通過HttpUrlConnection.getErrorStream()來查看服務器發送過來的信息。
InputStream getErrorStream()
例:
InputStream error = ((HttpURLConnection) connection).getErrorStream();
如果HTTP響應狀態為-1,就是出現了連接錯誤。HttpURLConnection會保持連接一直可用,如果你想關閉這個特性,需要把http.keepAlive設置為false:
System.setProperty("http.keepAlive", "false");
2.5 Http響應碼
HTTP響應的第一行被稱為消息響應。該信息不會被URLConnection的getHeaderFiled()方法讀取。
String getResponseMessage()
返回消息響應。如:HTTP/1.1 404 Not Found
int getResponseCode()
返回消息響應碼。如:404。
HttpURLConnection類提供了常見響應碼常量。具體請參考JDK文檔。
2.6 重定向
默認情況下,HttpURLConnection類會跟蹤重定向。但HttpURLConnection有兩個靜態方法,可以確定是否跟隨重定向。
static void setFollowRedirects(boolean set)
static boolean getFollowRedirects()
若跟隨,則為true,否則為false。
這兩個是靜態方法,它們會改變該方法后構造的所有HttpURLConnection對象的行為。若安全管理器不允許其改變,則會拋出SecurityException異常。
也可以對某個HttpURLConnection實例設置是否重定向。
void setInstanceFollowRedirects(boolean followRedirects)
boolean getInstanceFollowRedirects()
2.7 代理
HttpURLConnection可以得知請求是否通過了代理服務器。
使用如下方法:
abstract boolean usingProxy()
2.8 流模式
每個發送給HTTP服務器的請求都有HTTP首部。首部中有一個Content-type字段;即請求體中的字節數。首部位於主體的前面。但是,為了寫入首部,需要知道主體的長度,而在寫首部的時候可能還不知道主題的長度。一般情況下Java對此兩難境地的解決辦法是,對於從HttpURLConnection獲取的OutputStream,緩存寫入到此OutputStream中的所有內容,直到流被關閉。到這時,它就知道了主題中有多少字節,
在請求發送之前,HttpURLConnetion會把所有需要發送的數據放到緩沖區里,不管你是否使用connection.setRequestProperty("Content-Length", contentLength);設置了contentLength,當你並行發送大量的POST請求時,這可能會引起OutOfMemoryExceptions 異常,為了避免這個問題,需要使用如下方法:
void setFixedLengthStreamingMode(int contentLength)
此方法用於在預先已知內容長度時啟用沒有進行內部緩沖的 HTTP 請求正文的流。
例:
httpConnection.setFixedLengthStreamingMode(contentLength);
但是如果不能事先知道內容的長度,可以使用HttpURLConnection.setChunkedStreamingMode()方法設置為塊狀流模式。在塊狀流模式的情況下,放在塊里的內容將會被強行發送出去。下邊的例子將會把發送的內容按照每塊1KB的大小發送出去。
void setChunkedStreamingMode(int chunklen)
此方法用於在預先不知道內容長度時啟用沒有進行內部緩沖的 HTTP 請求正文的流。
例:
httpConnection.setChunkedStreamingMode(1024);
2.9 緩存
默認情況下,Java 1.5不返回任何數據。但通過派生java.net.ResponseCache類的子類,並安裝為系統默認值,就可以創建自己的緩存。無論系統合適嘗試通過協議處理器加載新的URL,它將首先在緩存中查找,若緩存找到了所需的內容,就不需要連接遠程服務器了。若緩存數據不存在,則下載相應數據,並將其放入緩存中。