posts - 495,comments - 227,trackbacks - 0
          <2006年4月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          30123456

          常用鏈接

          留言簿(46)

          隨筆分類(476)

          隨筆檔案(495)

          最新隨筆

          搜索

          •  

          積分與排名

          • 積分 - 1396480
          • 排名 - 16

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

           在上個(gè)專題《Java網(wǎng)絡(luò)編程之URI、URL研究(上)》中我們介紹了URI、URL的慨念和體系結(jié)構(gòu),以及如何使用URI在本文中我將繼續(xù)向大家介紹如何使用URL和MIME(多用途的網(wǎng)際郵件擴(kuò)充協(xié)議)的概念以及它如何與URL發(fā)生聯(lián)系的。

            使用URL
            網(wǎng)絡(luò)API通過提供URL類讓我們能在源代碼層使用URL。每一個(gè)URL對(duì)象都封裝了資源的標(biāo)識(shí)符和協(xié)議處理程序。前面的技巧顯示了獲得 URL對(duì)象的途徑之一是調(diào)用URI對(duì)象的toURL()方法。但是這種選擇不一定方便(為什么在需要URL對(duì)象的時(shí)候必須建立URI對(duì)象呢?)。作為代 替,你可以調(diào)用URL構(gòu)造函數(shù)來建立URL對(duì)象。你也可以調(diào)用URL的方法來提取URL的組件,打開一個(gè)輸入流(input stream)從資源中讀取信息,獲得某個(gè)能方便檢索資源數(shù)據(jù)的對(duì)象的引用,比較兩個(gè)URL對(duì)象中的URL,獲得到資源的連接對(duì)象,該連接對(duì)象允許代碼了 解(并寫入)更多的資源的信息。
            URL類有六個(gè)構(gòu)造函數(shù)。其中最簡單的是URL(String url),它有一個(gè)String類型的參數(shù),把URL分解為自己的組件,并把這些組件存儲(chǔ)在一個(gè)新的URL對(duì)象中。如果某個(gè)URL沒有包含協(xié)議處理程序或 該URL的協(xié)議是未知的,其它的五個(gè)構(gòu)造函數(shù)會(huì)產(chǎn)生一個(gè)java.net.MalformedURLException對(duì)象。
          下面的代碼片斷演示了使用URL(String url)建立一個(gè)URL對(duì)象,該對(duì)象封裝了一個(gè)簡單的URL組件和http協(xié)議處理程序。
          URL url = new URL ("
            一旦擁有了URL對(duì)象,你就可以使用getAuthority()、getDefaultPort()、 getFile()、 getHost()、 getPath()、getPort()、 getProtocol()、getQuery()、getRef()和getUserInfo(). The getDefaultPort()等方法提取各種組件。如果URL中沒有指定端口的部分,getDefaultPort()方法返回URL對(duì)象的協(xié)議處理 程序使用(資源定位)的默認(rèn)端口。getFile()方法返回路徑和查詢組件的結(jié)合體。getProtocol()方法返回決定資源的連接類型(例如 http、mailto、ftp)的協(xié)議的名稱。getRef()方法返回URL的部分片斷(我們所知道的引用)。最后,getUserInfo()方法 返回授權(quán)機(jī)構(gòu)組件的用戶信息部分。在這些URL組件提取方法中,如果某些組件不存在(如果沒有給URL對(duì)象的協(xié)議處理程序指定默認(rèn)的端口,它也返回- 1),這些方法就返回null或-1。
            作為這些組件提取方法的補(bǔ)充,你還可以調(diào)用openStream()方法檢索java.io.InputStream引用。使用這種引用,你可以用面向字節(jié)的方式讀取資源。
          列表4是URLDemo1的源代碼。該程序從命令行參數(shù)建立了一個(gè)URL對(duì)象,調(diào)用URL組件提取方法來檢索該URL的組件,調(diào)用URL的 openStream()方法打開與資源的連接并返回一個(gè)用于從資源讀取字節(jié)數(shù)據(jù)的InputStream引用,讀取/打印這些字節(jié),關(guān)閉輸入流。
            列表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 &copy; 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>

            在上面的信息中,輸出標(biāo)識(shí)符80是默認(rèn)端口,HTTP是協(xié)議。上面給出的是輸出的HTML頁面的源代碼。
          URL的openStream()方法通常返回抽象的InputStream類的一個(gè)具體的子類所建立的對(duì)象的引用。這意味著你必須按字節(jié)次序 讀取資源數(shù)據(jù),這種做法是恰當(dāng)?shù)模驗(yàn)槟悴恢缹⒁x取的數(shù)據(jù)是什么類型的。如果你事先知道要讀取的數(shù)據(jù)是文本的,并且每一行以換行符(\n)結(jié)束,你就 可以按行讀取而不是按字節(jié)讀取數(shù)據(jù)了。
            下面的代碼片斷演示了把一個(gè)InputStream對(duì)象包裝進(jìn)java.io.InputStreamReader對(duì)象以從8位過渡到16 位字符,把結(jié)果對(duì)象包裝進(jìn)java.io.BufferedReader對(duì)象以訪問BufferedReader的readLine()方法,并調(diào)用 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 ();
            有時(shí)候按字節(jié)的次序讀取數(shù)據(jù)并不方便。例如,如果資源是JPEG文件,那么獲取一個(gè)圖像處理過程并向該過程注冊(cè)一個(gè)用戶使用數(shù)據(jù)的方法更好。當(dāng)圖像完整下載后立即顯示它并不困難。如果出現(xiàn)這種情況,你就有必要使用getContent()方法。
            當(dāng)調(diào)用getContent()方法時(shí),它會(huì)返回某種對(duì)象的Object引用,而你可以調(diào)用該對(duì)象的方法(在轉(zhuǎn)換成適當(dāng)?shù)念愋秃螅捎酶奖愕姆绞綑z索數(shù)據(jù)。但是在調(diào)用該方法前,你必須使用instanceof驗(yàn)證對(duì)象的類型,防止類產(chǎn)生異常。
            對(duì)于JPEG資源,getContent()返回一個(gè)對(duì)象,該對(duì)象的類實(shí)現(xiàn)了java.awt.Image.ImageProducer接 口。下面的代碼片斷演示了使用instanceof驗(yàn)證對(duì)象是ImageProducer的,并進(jìn)行了轉(zhuǎn)換。接下來可以調(diào)用ImageProducer方 法注冊(cè)一個(gè)用戶并初始化圖像的使用過程。
          URL url = new URL (args [0]);
          Object o = url.getContent ();
          if (o instanceof ImageProducer)
          {
          ImageProducer ip = (ImageProducer) o;
          // ...
          }
            技巧
            調(diào)用URL的equals(Object o)和sameFile(Object o)方法來決定兩個(gè)URL是否相同。第一個(gè)方法包含了比較的片斷,而第二個(gè)方法沒有包含。你可以參閱SDK文檔查找更多信息。
            查看一下getContent()方法的源代碼,你會(huì)找到openConnection().getContent()。此外,查看一下 openStream()方法的源代碼,你會(huì)發(fā)現(xiàn)openConnection().getInputStream()。每個(gè)方法都首先調(diào)用URL的 openConnection()方法。這個(gè)方法返回抽象的java.net.URLConnection類(描述與某些資源的連接)的一個(gè)子類建立的對(duì) 象的引用。URLConnection的方法反映了資源和連接的細(xì)節(jié)信息,使我們能編寫代碼向資源寫入信息。
            列表5的URLDemo2源代碼演示了openConnection(),以及調(diào)用一些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]);
          // 返回代表某個(gè)資源的連接的新的特定協(xié)議對(duì)象的引用
          URLConnection uc = url.openConnection ();
          // 進(jìn)行連接
          uc.connect ();
          // 打印多種頭部字段的內(nèi)容
          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 ());
          }
          }?
          在 對(duì)openConnection()的調(diào)用返回后,調(diào)用了connect()方法--用于建立某種資源的連接。(盡管openConnection()方 法返回一個(gè)連接對(duì)象的引用,但是openConnection()不會(huì)連接到資源)。 URLConnection的getHeaderFields()方法返回一個(gè)對(duì)象的應(yīng)用,該對(duì)象的類實(shí)現(xiàn)了java.util.Map接口。該圖表 (map)包含頭部名稱和值的集合。什么是頭部(header)?頭部是基于文本的名稱/值對(duì),它識(shí)別資源數(shù)據(jù)的類型、數(shù)據(jù)的長度等等。
            在編譯了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?
            上面的輸出識(shí)別了很多頭部(包括Date、null、Content-Length、 Server、Last-Modified等等)和它們的值。輸出也顯示只允許從資源讀取數(shù)據(jù)。
            你對(duì)一個(gè)程序是如何識(shí)別資源數(shù)據(jù)的是否感到驚奇?仔細(xì)看一下前面的輸出,你會(huì)看到叫做Content-Type的東西。Content- Type是一個(gè)頭部,它識(shí)別了資源數(shù)據(jù)(內(nèi)容)的類型是text/html。text部分就是我們所知道的類型,html部分是我們所知道的子類型。(如 果內(nèi)容是普通的文本,Content-Type的值可能是text/plain。上面的類型表明內(nèi)容是文本的但不是沒有格式的)。Content- Type頭部是我們所知道的多用途Internet郵件擴(kuò)展(MIME)的一部分。
            MIME是傳統(tǒng)的傳輸消息的7位ASCII標(biāo)準(zhǔn)的一種擴(kuò)展。通過引入了多種頭部,MIME使視頻、聲音、圖像、不同字符集的文本與7位 ASCII結(jié)合起來。有了Content-Type,MIME可以識(shí)別Content-Length和其它標(biāo)準(zhǔn)的頭部。當(dāng)你使用 URLConnection類的時(shí)候,你會(huì)遇到getContentType()和getContentLength()。這些方法返回的值是 Content-Type和Content-Length頭部。
            你也許聽說過HTML窗體(<form>、 </form>)和其它的HTML標(biāo)記。窗體使我們能夠從某種資源得到(GET)數(shù)據(jù)并按后來的處理把HTML窗體的字段數(shù)據(jù)發(fā)送(POST)到某種資 源。你能夠使用URLConnection類和MIME模擬可以得到和發(fā)送數(shù)據(jù)的HTML窗體。下面說明你怎樣完成這種事務(wù)。
            假設(shè)你想把窗體數(shù)據(jù)發(fā)送(POST)到某個(gè)服務(wù)器程序。發(fā)送需要對(duì)窗體數(shù)據(jù)的操作。首先,窗體的數(shù)據(jù)必須組織為名稱/值對(duì) (name/value pair),其次每個(gè)對(duì)必須指定為name=value格式,再次如果發(fā)送多個(gè)名稱/值對(duì),必須使用 & 符號(hào)把每對(duì)分開,最后的name內(nèi)容和value的內(nèi)容必須使用application/x-www-form-urlencoded MIME類型編碼。例如x=y&a=b表現(xiàn)了兩個(gè)名稱/值對(duì)--x/y和a/b。
          為了輔助編碼,Java提供了java.net.URLEncoder類,它聲明了一對(duì)靜態(tài)的encode()方法。每個(gè)方法有一個(gè) String參數(shù)并返回包含已編碼的參數(shù)內(nèi)容的String對(duì)象的引用。例如,如果encode()發(fā)現(xiàn)參數(shù)中有空格,它在結(jié)果中用加號(hào)代替空格。
            下面的代碼片斷演示了調(diào)用URLEncoder的encode(String s)方法,對(duì)a 空格 b字符串進(jìn)行編碼。結(jié)果a+b存儲(chǔ)在一個(gè)新的String對(duì)象中,result引用它。
          String result = URLEncoder.encode ("a b");
            作為準(zhǔn)備窗體數(shù)據(jù)的補(bǔ)充,必須告訴URLConnection對(duì)象數(shù)據(jù)已經(jīng)被發(fā)送了,因?yàn)閁RLConnection默認(rèn)的操作是獲取數(shù) 據(jù)。為了完成這種事務(wù),你可以首先把openConnection()的返回值轉(zhuǎn)換為HttpURLConnection類型(在確保該返回值的類型正確 后)。接著調(diào)用結(jié)果對(duì)象的setRequestMethod(String method)方法,把POST作為method參數(shù)引用的對(duì)象的值。
            另一個(gè)必須完成的事務(wù)是調(diào)用URLConnection的setDoOutput(boolean doOutput)方法,其參數(shù)的值必須為true。這種事務(wù)是必要的,因?yàn)閁RLConnection對(duì)象在默認(rèn)情況下不支持輸出。(接著程序最終可以 調(diào)用URLConnection的getOutputStream()方法,為發(fā)送的窗體數(shù)據(jù)返回一個(gè)資源的輸出流的引用)。
            列表6是URLDemo3的源代碼,它演示了把窗體數(shù)據(jù)發(fā)送給某個(gè)"了解"application/x-www-form-urlencoded內(nèi)容類型的資源。它實(shí)現(xiàn)了前面提到的各種事務(wù)。
            列表6: URLDemo3.java
          // URLDemo3.java
          import java.io.*;
          import java.net.*;
          class URLDemo3
          {
          public static void main (String [] args) throws IOException
          {
          // 檢查最后兩個(gè)參數(shù)和參數(shù)的數(shù)量
          if (args.length < 2 || args.length % 2 != 0)
          {
          System.err.println ("usage: java URLDemo3 name value " +
          "[name value ...]");
          return;
          }
          // 建立程序連接服務(wù)器程序資源的URL對(duì)象,它返回一個(gè)窗體的名稱/值對(duì)
          URL url;
          url = new URL
          ("
          // 向某個(gè)特定協(xié)議對(duì)象返回表現(xiàn)http資源連接的引用
          URLConnection uc = url.openConnection ();
          // 驗(yàn)證連接的類型,必須是HttpURLConnection的
          if (!(uc instanceof HttpURLConnection))
          {
          System.err.println ("Wrong connection type");
          return;
          }
          // 表明程序必須把名稱/值對(duì)輸出到服務(wù)器程序資源
          uc.setDoOutput (true);
          // 表明只能返回有用的信息
          uc.setUseCaches (false);
          //設(shè)置Content-Type頭部指示指定URL已編碼數(shù)據(jù)的窗體MIME類型
          uc.setRequestProperty ("Content-Type",
          "application/x-www-form-urlencoded");
          // 建立名稱/值對(duì)內(nèi)容發(fā)送給服務(wù)器
          String content = buildContent (args);
          //設(shè)置Content-Type頭部指示指定URL已編碼數(shù)據(jù)的窗體MIME類型
          uc.setRequestProperty ("Content-Length",
          "" + content.length ());
          // 提取連接的適當(dāng)?shù)念愋?/div>
          HttpURLConnection hc = (HttpURLConnection) uc;
          // 把HTTP請(qǐng)求方法設(shè)置為POST(默認(rèn)的是GET)
          hc.setRequestMethod ("POST");
          // 輸出內(nèi)容
          OutputStream os = uc.getOutputStream ();
          DataOutputStream dos = new DataOutputStream (os);
          dos.writeBytes (content);
          dos.flush ();
          dos.close ();
          // 從服務(wù)器程序資源輸入和顯示內(nèi)容
          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++)
          {
          // 為正確的傳輸對(duì)參數(shù)編碼
          String encodedItem = URLEncoder.encode (args [i]);
          sb.append (encodedItem);
          if (i % 2 == 0)
          sb.append ("="); // 分離名稱和值
          else
          sb.append ("&"); // 分離名稱/值對(duì)
          }
          // 刪除最后的 & 間隔符
          sb.setLength (sb.length () - 1);
          return sb.toString ();
          }
          }
            你可以會(huì)奇怪為什么URLDemo3沒有調(diào)用URLConnection的connect()的方法。這個(gè)方法沒有被明顯的調(diào)用,因?yàn)槿绻? 連向資源的連接沒有建立的話,其它的URLConnection方法(例如getContentLength())會(huì)明確的調(diào)用connect()方法。 但是一旦連建立了接,調(diào)用這些方法(例如setDoOutput(boolean doOutput))就是違反規(guī)定的。在connect()被(明確地或隱含地)調(diào)用后,這些方法會(huì)產(chǎn)生一個(gè)IllegalStateException 對(duì)象。
          在URLDemo3編譯后,在命令行輸入java URLDemo3 name1 value1 name2 value2 name3 value3,你可以看到下面的輸出:
          <html> <head>
          <title>Echoing your name value pairs</title>
          </head>
          <body>
          <o(jì)l>
          <li>name1 : value1
          <li>name2 : value2
          <li>name3 : value3
          </ol>
          <hr>
          Mon Feb 18 08:58:45 2002
          </body>
          </html>
            該服務(wù)器程序資源的輸出由HTML組成,這些HTML回應(yīng)的是name1、value1、name2、 value2、name3和value3。
            技巧
            如果你需要URL對(duì)象的URL的字符串表現(xiàn)形式,請(qǐng)調(diào)用toExternalForm()或toString()。兩種方法的功能是相同的。
            總結(jié)
            本文研究了Java的網(wǎng)絡(luò)API,聚焦于URI、URL和URN。你學(xué)習(xí)了這些概念,以及怎樣使用URI和URL(URL相關(guān))的類工作,同時(shí)你學(xué)習(xí)了MIME的知識(shí)以及它與URL的關(guān)系。現(xiàn)在你應(yīng)該編寫一些代碼熟悉一下所學(xué)的內(nèi)容了。
          主站蜘蛛池模板: 嘉义县| 石家庄市| 阜新市| 岫岩| 甘谷县| 开鲁县| 东乡| 曲阜市| 方正县| 汽车| 泰州市| 叙永县| 哈巴河县| 宝山区| 璧山县| 策勒县| 怀安县| 平度市| 桃江县| 龙山县| 德庆县| 图木舒克市| 永和县| 清水河县| 苏尼特右旗| 沂南县| 金华市| 铜川市| 沙田区| 烟台市| 陇西县| 洞口县| 芜湖市| 泸西县| 奉贤区| 新津县| 衡水市| 呼玛县| 水城县| 如皋市| 普陀区|