posts - 495,comments - 227,trackbacks - 0
           在上個專題《Java網絡編程之URI、URL研究(上)》中我們介紹了URI、URL的慨念和體系結構,以及如何使用URI在本文中我將繼續向大家介紹如何使用URL和MIME(多用途的網際郵件擴充協議)的概念以及它如何與URL發生聯系的。

            使用URL
            網絡API通過提供URL類讓我們能在源代碼層使用URL。每一個URL對象都封裝了資源的標識符和協議處理程序。前面的技巧顯示了獲得 URL對象的途徑之一是調用URI對象的toURL()方法。但是這種選擇不一定方便(為什么在需要URL對象的時候必須建立URI對象呢?)。作為代 替,你可以調用URL構造函數來建立URL對象。你也可以調用URL的方法來提取URL的組件,打開一個輸入流(input stream)從資源中讀取信息,獲得某個能方便檢索資源數據的對象的引用,比較兩個URL對象中的URL,獲得到資源的連接對象,該連接對象允許代碼了 解(并寫入)更多的資源的信息。
            URL類有六個構造函數。其中最簡單的是URL(String url),它有一個String類型的參數,把URL分解為自己的組件,并把這些組件存儲在一個新的URL對象中。如果某個URL沒有包含協議處理程序或 該URL的協議是未知的,其它的五個構造函數會產生一個java.net.MalformedURLException對象。
          下面的代碼片斷演示了使用URL(String url)建立一個URL對象,該對象封裝了一個簡單的URL組件和http協議處理程序。
          URL url = new URL ("
            一旦擁有了URL對象,你就可以使用getAuthority()、getDefaultPort()、 getFile()、 getHost()、 getPath()、getPort()、 getProtocol()、getQuery()、getRef()和getUserInfo(). The getDefaultPort()等方法提取各種組件。如果URL中沒有指定端口的部分,getDefaultPort()方法返回URL對象的協議處理 程序使用(資源定位)的默認端口。getFile()方法返回路徑和查詢組件的結合體。getProtocol()方法返回決定資源的連接類型(例如 http、mailto、ftp)的協議的名稱。getRef()方法返回URL的部分片斷(我們所知道的引用)。最后,getUserInfo()方法 返回授權機構組件的用戶信息部分。在這些URL組件提取方法中,如果某些組件不存在(如果沒有給URL對象的協議處理程序指定默認的端口,它也返回- 1),這些方法就返回null或-1。
            作為這些組件提取方法的補充,你還可以調用openStream()方法檢索java.io.InputStream引用。使用這種引用,你可以用面向字節的方式讀取資源。
          列表4是URLDemo1的源代碼。該程序從命令行參數建立了一個URL對象,調用URL組件提取方法來檢索該URL的組件,調用URL的 openStream()方法打開與資源的連接并返回一個用于從資源讀取字節數據的InputStream引用,讀取/打印這些字節,關閉輸入流。
            列表4: URLDemo1.java
          // URLDemo1.java
          import java.io.*;
          import java.net.*;
          class URLDemo1
          {
          public static void main (String [] args) throws IOException
          {
          if (args.length != 1)
          {
          System.err.println ("usage: java URLDemo1 url");
          return;
          }
          URL url = new URL (args [0]);
          System.out.println ("Authority = "+ url.getAuthority ());
          System.out.println ("Default port = " +url.getDefaultPort ());
          System.out.println ("File = " +url.getFile ());
          System.out.println ("Host = " +url.getHost ());
          System.out.println ("Path = " +url.getPath ());
          System.out.println ("Port = " +url.getPort ());
          System.out.println ("Protocol = " +url.getProtocol ());
          System.out.println ("Query = " +url.getQuery ());
          System.out.println ("Ref = " +url.getRef ());
          System.out.println ("User Info = " +url.getUserInfo ());
          System.out.print ('\n');
          InputStream is = url.openStream ();
          int ch;
          while ((ch = is.read ()) != -1)
          System.out.print ((char) ch);
          is.close ();
          }
          }
          Authority = http://www.javajeff.com
          Default port = 80
          File = /articles/articles.html
          Host = http://www.javajeff.com
          Path = /articles/articles.html
          Port = -1
          Protocol = http
          Query = null
          Ref = null
          User Info = null
          <html>
          <head>
          <title>
          Java Jeff - Articles
          </title>
          <meta http-equiv=Content-Type content="text/html;
          charset=ISO-8859-1">
          <meta name=author content="Jeff Friesen">
          <meta name=keywords content="java, virtual machine">
          <script language=JavaScript>
          if (navigator.appName == "Netscape")
          document.write ("<br>");
          </script>
          </head>
          <body bgcolor=#000000>
          <center>
          <table border=1 cellpadding=5 cellspacing=0>
          <tr>
          <td>
          <table cellpadding=0 cellspacing=0>
          <tr>
          <td>
          <a href=informit/informit.html>
          <img alt=InformIT border=0 src=informit.gif></a>
          </td>
          </tr>
          </table>
          </td>
          <td align=middle>
          <img src=title.gif><br>
          <a href=../welcome/welcome.html>
          <img alt="Welcome to Java Jeff!" border=0 src=jupiter.jpg>
          </a><br>
          <img src=../common/clear_dot.gif vspace=5><br>
          <a href=../ads/ads.html>
          <img alt="Welcome to Java Jeff!" border=0
          src=jupiter.jpg>
          </td>
          <td>
          <table cellpadding=0 cellspacing=0>
          <tr>
          <td>
          <a href=javaworld/javaworld.html>
          <img alt=JavaWorld border=0 src=javaworld.gif></a>
          </td>
          </tr>
          </table>
          </td>
          </tr>
          </table>
          </center>
          <br>
          <font color=#ffffff>
          <center>
          Best viewed at a resolution of 1024x768 or higher.<br>
          <img src=../common/clear_dot.gif vspace=5><br>
          <i>
          Copyright © 2001-2002, Jeff Friesen. All rights
          reserved.
          </i>
          <p>
          <a href=../index.html>
          <img alt=Back border=0 src=../common/back.gif></a>
          </center>
          </font>
          </body>
          </html>

            在上面的信息中,輸出標識符80是默認端口,HTTP是協議。上面給出的是輸出的HTML頁面的源代碼。
          URL的openStream()方法通常返回抽象的InputStream類的一個具體的子類所建立的對象的引用。這意味著你必須按字節次序 讀取資源數據,這種做法是恰當的,因為你不知道將要讀取的數據是什么類型的。如果你事先知道要讀取的數據是文本的,并且每一行以換行符(\n)結束,你就 可以按行讀取而不是按字節讀取數據了。
            下面的代碼片斷演示了把一個InputStream對象包裝進java.io.InputStreamReader對象以從8位過渡到16 位字符,把結果對象包裝進java.io.BufferedReader對象以訪問BufferedReader的readLine()方法,并調用 readLine()方法從資源讀取文本的所有行。
          InputStream is = url.openStream ();
          BufferedReader br = new BufferedReader (new InputStreamReader (is));
          String line;
          while ((line = br.readLine ()) != null)
          System.out.println (line);
          is.close ();
            有時候按字節的次序讀取數據并不方便。例如,如果資源是JPEG文件,那么獲取一個圖像處理過程并向該過程注冊一個用戶使用數據的方法更好。當圖像完整下載后立即顯示它并不困難。如果出現這種情況,你就有必要使用getContent()方法。
            當調用getContent()方法時,它會返回某種對象的Object引用,而你可以調用該對象的方法(在轉換成適當的類型后),采用更方便的方式檢索數據。但是在調用該方法前,你必須使用instanceof驗證對象的類型,防止類產生異常。
            對于JPEG資源,getContent()返回一個對象,該對象的類實現了java.awt.Image.ImageProducer接 口。下面的代碼片斷演示了使用instanceof驗證對象是ImageProducer的,并進行了轉換。接下來可以調用ImageProducer方 法注冊一個用戶并初始化圖像的使用過程。
          URL url = new URL (args [0]);
          Object o = url.getContent ();
          if (o instanceof ImageProducer)
          {
          ImageProducer ip = (ImageProducer) o;
          // ...
          }
            技巧
            調用URL的equals(Object o)和sameFile(Object o)方法來決定兩個URL是否相同。第一個方法包含了比較的片斷,而第二個方法沒有包含。你可以參閱SDK文檔查找更多信息。
            查看一下getContent()方法的源代碼,你會找到openConnection().getContent()。此外,查看一下 openStream()方法的源代碼,你會發現openConnection().getInputStream()。每個方法都首先調用URL的 openConnection()方法。這個方法返回抽象的java.net.URLConnection類(描述與某些資源的連接)的一個子類建立的對 象的引用。URLConnection的方法反映了資源和連接的細節信息,使我們能編寫代碼向資源寫入信息。
            列表5的URLDemo2源代碼演示了openConnection(),以及調用一些URLConnection的方法。
            列表5: URLDemo2.java
          // URLDemo2.java
          import java.io.*;
          import java.net.*;
          import java.util.*;
          class URLDemo2
          {
          public static void main (String [] args) throws IOException
          {
          if (args.length != 1)
          {
          System.err.println ("usage: java URLDemo2 url");
          return;
          }
          URL url = new URL (args [0]);
          // 返回代表某個資源的連接的新的特定協議對象的引用
          URLConnection uc = url.openConnection ();
          // 進行連接
          uc.connect ();
          // 打印多種頭部字段的內容
          Map m = uc.getHeaderFields ();
          Iterator i = m.entrySet ().iterator ();
          while (i.hasNext ())
          System.out.println (i.next ());
          // 如果資源允許輸入和輸出操作就找出來
          System.out.println ("Input allowed = " +uc.getDoInput ());
          System.out.println ("Output allowed = " +uc.getDoOutput ());
          }
          }?
          在 對openConnection()的調用返回后,調用了connect()方法--用于建立某種資源的連接。(盡管openConnection()方 法返回一個連接對象的引用,但是openConnection()不會連接到資源)。 URLConnection的getHeaderFields()方法返回一個對象的應用,該對象的類實現了java.util.Map接口。該圖表 (map)包含頭部名稱和值的集合。什么是頭部(header)?頭部是基于文本的名稱/值對,它識別資源數據的類型、數據的長度等等。
            在編譯了URLDemo2后,在命令行輸入java URLDemo2 http://www.javajeff.com,輸出如下:
          Date=[Sun, 17 Feb 2002 17:49:32 GMT]
          Connection=[Keep-Alive]
          Content-Type=[text/html; charset=iso-8859-1]
          Accept-Ranges=[bytes]
          Content-Length=[7214]
          null=[HTTP/1.1 200 OK]
          ETag=["4470e-1c2e-3bf29d5a"]
          Keep-Alive=[timeout=15, max=100]
          Server=[Apache/1.3.19 (Unix) Debian/GNU]
          Last-Modified=[Wed, 14 Nov 2001 16:35:38 GMT]
          Input allowed = true
          Output allowed = false?
            上面的輸出識別了很多頭部(包括Date、null、Content-Length、 Server、Last-Modified等等)和它們的值。輸出也顯示只允許從資源讀取數據。
            你對一個程序是如何識別資源數據的是否感到驚奇?仔細看一下前面的輸出,你會看到叫做Content-Type的東西。Content- Type是一個頭部,它識別了資源數據(內容)的類型是text/html。text部分就是我們所知道的類型,html部分是我們所知道的子類型。(如 果內容是普通的文本,Content-Type的值可能是text/plain。上面的類型表明內容是文本的但不是沒有格式的)。Content- Type頭部是我們所知道的多用途Internet郵件擴展(MIME)的一部分。
            MIME是傳統的傳輸消息的7位ASCII標準的一種擴展。通過引入了多種頭部,MIME使視頻、聲音、圖像、不同字符集的文本與7位 ASCII結合起來。有了Content-Type,MIME可以識別Content-Length和其它標準的頭部。當你使用 URLConnection類的時候,你會遇到getContentType()和getContentLength()。這些方法返回的值是 Content-Type和Content-Length頭部。
            你也許聽說過HTML窗體(<form>、 </form>)和其它的HTML標記。窗體使我們能夠從某種資源得到(GET)數據并按后來的處理把HTML窗體的字段數據發送(POST)到某種資 源。你能夠使用URLConnection類和MIME模擬可以得到和發送數據的HTML窗體。下面說明你怎樣完成這種事務。
            假設你想把窗體數據發送(POST)到某個服務器程序。發送需要對窗體數據的操作。首先,窗體的數據必須組織為名稱/值對 (name/value pair),其次每個對必須指定為name=value格式,再次如果發送多個名稱/值對,必須使用 & 符號把每對分開,最后的name內容和value的內容必須使用application/x-www-form-urlencoded MIME類型編碼。例如x=y&a=b表現了兩個名稱/值對--x/y和a/b。
          為了輔助編碼,Java提供了java.net.URLEncoder類,它聲明了一對靜態的encode()方法。每個方法有一個 String參數并返回包含已編碼的參數內容的String對象的引用。例如,如果encode()發現參數中有空格,它在結果中用加號代替空格。
            下面的代碼片斷演示了調用URLEncoder的encode(String s)方法,對a 空格 b字符串進行編碼。結果a+b存儲在一個新的String對象中,result引用它。
          String result = URLEncoder.encode ("a b");
            作為準備窗體數據的補充,必須告訴URLConnection對象數據已經被發送了,因為URLConnection默認的操作是獲取數 據。為了完成這種事務,你可以首先把openConnection()的返回值轉換為HttpURLConnection類型(在確保該返回值的類型正確 后)。接著調用結果對象的setRequestMethod(String method)方法,把POST作為method參數引用的對象的值。
            另一個必須完成的事務是調用URLConnection的setDoOutput(boolean doOutput)方法,其參數的值必須為true。這種事務是必要的,因為URLConnection對象在默認情況下不支持輸出。(接著程序最終可以 調用URLConnection的getOutputStream()方法,為發送的窗體數據返回一個資源的輸出流的引用)。
            列表6是URLDemo3的源代碼,它演示了把窗體數據發送給某個"了解"application/x-www-form-urlencoded內容類型的資源。它實現了前面提到的各種事務。
            列表6: URLDemo3.java
          // URLDemo3.java
          import java.io.*;
          import java.net.*;
          class URLDemo3
          {
          public static void main (String [] args) throws IOException
          {
          // 檢查最后兩個參數和參數的數量
          if (args.length < 2 || args.length % 2 != 0)
          {
          System.err.println ("usage: java URLDemo3 name value " +
          "[name value ...]");
          return;
          }
          // 建立程序連接服務器程序資源的URL對象,它返回一個窗體的名稱/值對
          URL url;
          url = new URL
          ("
          // 向某個特定協議對象返回表現http資源連接的引用
          URLConnection uc = url.openConnection ();
          // 驗證連接的類型,必須是HttpURLConnection的
          if (!(uc instanceof HttpURLConnection))
          {
          System.err.println ("Wrong connection type");
          return;
          }
          // 表明程序必須把名稱/值對輸出到服務器程序資源
          uc.setDoOutput (true);
          // 表明只能返回有用的信息
          uc.setUseCaches (false);
          //設置Content-Type頭部指示指定URL已編碼數據的窗體MIME類型
          uc.setRequestProperty ("Content-Type",
          "application/x-www-form-urlencoded");
          // 建立名稱/值對內容發送給服務器
          String content = buildContent (args);
          //設置Content-Type頭部指示指定URL已編碼數據的窗體MIME類型
          uc.setRequestProperty ("Content-Length",
          "" + content.length ());
          // 提取連接的適當的類型
          HttpURLConnection hc = (HttpURLConnection) uc;
          // 把HTTP請求方法設置為POST(默認的是GET)
          hc.setRequestMethod ("POST");
          // 輸出內容
          OutputStream os = uc.getOutputStream ();
          DataOutputStream dos = new DataOutputStream (os);
          dos.writeBytes (content);
          dos.flush ();
          dos.close ();
          // 從服務器程序資源輸入和顯示內容
          InputStream is = uc.getInputStream ();
          int ch;
          while ((ch = is.read ()) != -1)
          System.out.print ((char) ch);
          is.close ();
          }
          static String buildContent (String [] args)
          {
          StringBuffer sb = new StringBuffer ();
          for (int i = 0; i < args.length; i++)
          {
          // 為正確的傳輸對參數編碼
          String encodedItem = URLEncoder.encode (args [i]);
          sb.append (encodedItem);
          if (i % 2 == 0)
          sb.append ("="); // 分離名稱和值
          else
          sb.append ("&"); // 分離名稱/值對
          }
          // 刪除最后的 & 間隔符
          sb.setLength (sb.length () - 1);
          return sb.toString ();
          }
          }
            你可以會奇怪為什么URLDemo3沒有調用URLConnection的connect()的方法。這個方法沒有被明顯的調用,因為如果 連向資源的連接沒有建立的話,其它的URLConnection方法(例如getContentLength())會明確的調用connect()方法。 但是一旦連建立了接,調用這些方法(例如setDoOutput(boolean doOutput))就是違反規定的。在connect()被(明確地或隱含地)調用后,這些方法會產生一個IllegalStateException 對象。
          在URLDemo3編譯后,在命令行輸入java URLDemo3 name1 value1 name2 value2 name3 value3,你可以看到下面的輸出:
          <html> <head>
          <title>Echoing your name value pairs</title>
          </head>
          <body>
          <ol>
          <li>name1 : value1
          <li>name2 : value2
          <li>name3 : value3
          </ol>
          <hr>
          Mon Feb 18 08:58:45 2002
          </body>
          </html>
            該服務器程序資源的輸出由HTML組成,這些HTML回應的是name1、value1、name2、 value2、name3和value3。
            技巧
            如果你需要URL對象的URL的字符串表現形式,請調用toExternalForm()或toString()。兩種方法的功能是相同的。
            總結
            本文研究了Java的網絡API,聚焦于URI、URL和URN。你學習了這些概念,以及怎樣使用URI和URL(URL相關)的類工作,同時你學習了MIME的知識以及它與URL的關系。現在你應該編寫一些代碼熟悉一下所學的內容了。
          主站蜘蛛池模板: 昭苏县| 景德镇市| 鱼台县| 安泽县| 济阳县| 铁岭县| 盐源县| 鹤山市| 焉耆| 舟山市| 岑溪市| 依安县| 隆回县| 澳门| 卢龙县| 福州市| 新余市| 泰顺县| 邹城市| 林口县| 无极县| 乌兰察布市| 昭觉县| 全椒县| 天镇县| 英超| 雷波县| 五常市| 忻州市| 石阡县| 沁水县| 绥江县| 宜丰县| 门头沟区| 潼南县| 永和县| 兴和县| 惠州市| 嘉峪关市| 南昌县| 广东省|