URI:Uniform Resource Identifier,統(tǒng)一資源標識符;
URL:Uniform Resource Locator,統(tǒng)一資源定位符;
URN:Uniform Resource Name,統(tǒng)一資源名稱。
其中,URL,URN是URI的子集。
Web上地址的基本形式是URI,它代表統(tǒng)一資源標識符。有兩種形式:
URL:目前URI的最普遍形式就是無處不在的URL或統(tǒng)一資源定位器。
URN:URL的一種更新形式,統(tǒng)一資源名稱(URN, Uniform Resource Name)不依賴于位置,并且有可能減少失效連接的個數(shù)。但是其流行還需假以時日,因為它需要更精密軟件的支持。
URI是以某種統(tǒng)一的(標準化的)方式標識資源的簡單字符串。
URI一般由三部分組成:
1. 訪問資源的命名機制。
2. 存放資源的主機名。
3. 資源自身的名稱,由路徑表示。
典型情況下,這種字符串以scheme(命名URI的名字空間的標識符——一組相關(guān)的名稱)開頭,語法如下:
[scheme:] scheme-specific-part
URI以scheme和冒號開頭。Scheme用大寫/小寫字母開頭,后面為空或者跟著更多的大寫/小寫字母、數(shù)字、加號、減號和點號。冒號把scheme與scheme-specific-part分開了,并且scheme-specific-part的語法和語義(意思)由URI的名字空間決定。如下面的例子:
http://www.cnn.com,其中http是scheme,//www.cnn.com是 scheme-specific-part,并且它的scheme與scheme-specific-part被冒號分開了。
URI有絕對和相對之分,絕對的URI指以scheme(后面跟著冒號)開頭的URI。前面提到的http://www.cnn.com就是絕對的URI的一個例子,其它的例子還有mailto:jeff@javajeff.com、news:comp.lang.java.help和xyz://whatever。你可以把絕對的URI看作是以某種方式引用某種資源,而這種方式對標識符出現(xiàn)的環(huán)境沒有依賴。如果使用文件系統(tǒng)作類比,絕對的URI類似于從根目錄開始的某個文件的徑。
與絕對的URI不同的,相對的URI不是以scheme(后面跟著冒號)開始的URI。 它的一個例子是articles/articles.html。你可以把相對的URI看作是以某種方式引用某種資源,而這種方式依賴于標識符出現(xiàn)的環(huán)境。如果用文件系統(tǒng)作類比,相對的URI類似于從當(dāng)前目錄開始的文件路徑。
URL是Uniform Resource Location的縮寫,譯為"統(tǒng)一資源定位符"。通俗地說,URL是Internet上用來描述信息資源的字符串,主要用在各種WWW客戶程序和服務(wù)器程序上,特別是著名的Mosaic。采用URL可以用一種統(tǒng)一的格式來描述各種信息資源,包括文件、服務(wù)器的地址和目錄等。
URL的格式由下列三部分組成:
第一部分是協(xié)議(或稱為服務(wù)方式);
第二部分是存有該資源的主機IP地址(有時也包括端口號);
第三部分是主機資源的具體地址。,如目錄和文件名等。
第一部分和第二部分之間用"://"符號隔開,第二部分和第三部分用"/"符號隔開。第一部分和第二部分是不可缺少的,第三部分有時可以省略。
目前最大的缺點是當(dāng)信息資源的存放地點發(fā)生變化時,必須對URL作相應(yīng)的改變。因此人們正在研究新的信息資源表示方法,例如:URI(Universal Resource Identifier)即"通用資源標識"(參見RFC 1630)、URN(Uniform Resource Name)即"統(tǒng)一資源名"和URC(Uniform Resource Citation)即"統(tǒng)一資源引用符"等。
URI目前還處在進一步的研究當(dāng)中。研究的方向就是彌補URL目前存在的缺點。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
JAVA中的URI,URL,URN
關(guān)鍵字: URL,URI,URN
URI、URL和URN是識別、定位和命名網(wǎng)上資源的標準途徑。本文分析了URI、URL和URN的概念,以及Java的URI和URL類(以及與URL相關(guān)的類),并演示了如何在程序中使用這些類。
Internet被認為是全球的實際和抽象的資源的集合。實際的資源包括從文件(file)到人(person),抽象的資源包括數(shù)據(jù)庫查詢等。因為要通過多樣的方式識別資源,所以需要標準的識別Internet資源的途徑。為了滿足這種需要,引入了URI、URL和URN。
URI、URL和URN的概念
URI
URI = Uniform Resource Identifier
There are two types of URIs: URLs and URNs.
See RFC 1630: Universal Resource Identifiers in WWW: A Unifying Syntax for the Expression of Names and Addresses of Objects on the Network as used in the WWW.
URL
URL = Uniform Resource Locator
See RFC 1738: Uniform Resource Locators (URL)
URN
URN = Uniform Resource Name.
URI、URL和URN是彼此關(guān)聯(lián)的。URI位于頂層,URL和URN的范疇位于底層。URL和URN都是URI的子范疇。
URI翻譯為統(tǒng)一資源標識,它是以某種標準化的方式標識資源的字符串。這種字符串以scheme開頭,語法如下:
[scheme:] scheme-specific-part
URI以scheme和冒號開頭。冒號把scheme與scheme-specific-part分開,并且scheme-specific-part的語法由URI的scheme決定。例如http://www.cnn.com,其中http是scheme,//www.cnn.com是 scheme-specific-part。
URI分為絕對(absolute)或相對(relative)兩類。絕對URI指以scheme(后面跟著冒號)開頭的URI。前面提到的http://www.cnn.com就是絕對的URI的一個例子,其它的例子還有mailto:jeff@javajeff.com、news:comp.lang.java.help和xyz://whatever??梢园呀^對URI看作是以某種方式引用某種資源,而對環(huán)境沒有依賴。如果使用文件系統(tǒng)作類比,絕對URI類似于從根目錄開始的某個文件的路徑。相對URI不以scheme開始,一個例子是articles/articles.html??梢园严鄬RI看作是以某種方式引用某種資源,而這種方式依賴于標識符出現(xiàn)的環(huán)境。如果用文件系統(tǒng)作類比,相對URI類似于從當(dāng)前目錄開始的文件路徑。
URI可以進一步分為不透明的(opaque)和分層(hierarchical)的兩類。不透明的URI指scheme-specific-part不是以‘/’開頭的絕對的URI。其例子有news:comp.lang.java和前面的mailto:jeff@javajeff.com。不透明的URI不能做進一步的解析,不需要驗證scheme-specific-part的有效性。與它不同的是,分層的URI是以‘/’開頭的絕對的URI或相對的URL。分層的URI的scheme-specific-part必須被分解為幾個組成部分。分層的URI的scheme-specific-part必須符合下面的語法:
[//authority] [path] [?query] [#fragment]
可選的授權(quán)機構(gòu)(authority)標識了該URI名字空間的命名機構(gòu)。如果有這一部分則以‘//’開始。它可以是基于服務(wù)器或基于授權(quán)機構(gòu)的?;谑跈?quán)機構(gòu)有特定的語法(本文沒有討論,因為很少使用它),而基于服務(wù)器的語法如下:
[userinfo@] host [:port]
基于服務(wù)器的authority以用戶信息(例如用戶名)開始,后面跟著一個@符號,緊接著是主機的名稱,以及冒號和端口號。例如jeff@x.com:90就是一個基于服務(wù)器的authority,其中jeff為用戶信息,x.com為主機,90為端口。
可選的path根據(jù)authority(如果提供了)或schema(如果沒有authority)定義資源的位置。路徑(path)可以分成一系列的路徑片斷(path segment),每個路徑片斷使用‘/’與其它片斷隔開。如果第一個路徑片斷以‘/’開始,該路徑就被認為是絕對的,否則路徑就被認為是相對的。例如,/a/b/c由三個路徑片斷a、b和c組成,此外這個路徑是絕對的,因為第一個路徑片斷(a)的前綴是‘/’。
可選的query定義要傳遞給資源的查詢信息。資源使用該信息獲取或生成其它的的數(shù)據(jù)傳遞回調(diào)用者。例如,http://www.somesite.net/a?x=y, x=y就是一個query,在這個查詢中x是某種實體的名稱,y是該實體的值。
最后一個部分是fragment。當(dāng)使用URI進行某種檢索操作時,后面執(zhí)行操作的軟件使用fragment聚焦于軟件感興趣的資源部分。
分析一個例子ftp://george@x.com:90/public/notes?text=shakespeare#hamlet
上面的URI把ftp識別為schema,把george@x.com:90識別為基于服務(wù)器的authority(其中g(shù)eorge是用戶信息,x.com是主機,90是端口),把/public/notes識別為路徑,把text=shakespeare識別為查詢,把hamlet識別為片斷。本質(zhì)上它是一個叫做george的用戶希望通過/public/notes路徑在服務(wù)器x.com的90端口上檢索shakespeare文本的hamlet信息。
URI的標準化(normalize)
標準化可以通過目錄術(shù)語來理解。假定目錄x直接位于根目錄之下,x有子目錄a和b,b有文件memo.txt,a是當(dāng)前目錄。為了顯示memo.txt中的內(nèi)容,你可能輸入type \x\.\b\memo.txt。你也可能輸入type \x\a\..\b\memo.txt,在這種情況下,a和..的出現(xiàn)是沒有必要的。這兩種形式都不是最簡單的。但是如果輸入\x\b\memo.txt,你就指定了最簡單的路徑了,從根目錄開始訪問memo.txt。最簡單的\x\b\memo.txt路徑就是標準化的路徑。
通常通過base + relative URI訪問資源。Base URI是絕對URI,而Relative URI標識了與Base URI相對的資源。因此有必要把兩種URI通過解析過程合并,相反地從合并的URI中提取Relative URI也是可行的。
假定把x://a/作為Base URI,并把b/c作為Relative URI。Resolve這個相對URI將產(chǎn)生x://a/b/c。根據(jù)x://a/相對化(Relative)x://a/b/c將產(chǎn)生b/c。
URI不能讀取/寫入資源,這是統(tǒng)一的資源定位器(URL)的任務(wù)。URL是一種URI,它的schema是已知的網(wǎng)絡(luò)協(xié)議,并且它把URI與某種協(xié)議處理程序聯(lián)系起來(一種與資源通訊的讀/寫機制)。
URI一般不能為資源提供持久不變的名稱。這是統(tǒng)一的資源命名(URN)的任務(wù)。URN也是一種URI,但是全球唯一的、持久不便的,即使資源不再存在或不再使用。
使用URI
Java API通過提供URI類(位于java.net包中),使我們在代碼中使用URI成為可能。URI的構(gòu)造函數(shù)建立URI對象,并且分析URI字符串,提取URI組件。URI的方法提供了如下功能:1)決定URI對象的URI是絕對的還是相對的;2)決定URI對象是opaque還是hierarchical;3)比較兩個URI對象;4)標準化(normalize)URI對象;5)根據(jù)Base URI解析某個Relative URI;6)根據(jù)Base URI計算某個URI的相對URI;7)把URI對象轉(zhuǎn)換為URL對象。
在URI里面有多個構(gòu)造函數(shù),最簡單的是URI(String uri)。這個構(gòu)造函數(shù)把String類型的參數(shù)URI分解為組件,并把這些組件存儲在新的URI對象中。如果String對象的URI違反了RFC 2396的語法規(guī)則,將會產(chǎn)生一個java.net.URISyntaxException。
下面的代碼演示了使用URI(String uri)建立URI對象: 下面的代碼片斷演示了parseServerAuthority(): 程序1: URIDemo1.java public class URIDemo1 { System.out.println ("Authority = " +uri.getAuthority ()); URI類支持基本的操作,包括標準化(normalize)、分解(resolution)和相對化(relativize)。下例演示了normalize()方法。 程序2: URIDemo2.java class URIDemo2 { 上面的輸出顯示y、..和.消失了。 程序3: URIDemo3.java class URIDemo3 { URI uriBase = new URI (args [0]); URI uriRelative = new URI (args [1]); URI uriResolved = uriBase.resolve (uriRelative); URI uriRelativized = uriBase.relativize (uriResolved); Java提供了URL類,每一個URL對象都封裝了資源標識符和協(xié)議處理程序。獲得URL對象的途徑之一是調(diào)用URI的toURL()方法,也可以直接調(diào)用URL的構(gòu)造函數(shù)來建立URL對象。 URL類有多個構(gòu)造函數(shù)。其中最簡單的是URL(String url),它有一個String類型的參數(shù)。如果某個URL沒有包含協(xié)議處理程序或該URL的協(xié)議是未知的,其它的構(gòu)造函數(shù)會產(chǎn)生一個java.net.MalformedURLException。 程序4: URLDemo1.java class URLDemo1 { URL url = new URL (args [0]); System.out.println ("Authority = "+ url.getAuthority ()); System.out.print ('\n'); InputStream is = url.openStream (); int ch; URL的openStream()方法返回的InputStream類型,這意味著你必須按字節(jié)次序讀取資源數(shù)據(jù),這種做法是恰當(dāng)?shù)?,因為你不知道將要讀取的數(shù)據(jù)是什么類型。如果你事先知道要讀取的數(shù)據(jù)是文本,并且每一行以換行符(\n)結(jié)束,你就可以按行讀取而不是按字節(jié)讀取數(shù)據(jù)了。 下面的代碼片斷演示了把一個InputStream對象包裝進InputStreamReader以從8位過渡到16位字符,進而把結(jié)果對象包裝進BufferedReader以調(diào)用其readLine()方法。 當(dāng)調(diào)用getContent()方法時,它會返回某種對象的引用,而你可以調(diào)用該對象的方法(在轉(zhuǎn)換成適當(dāng)?shù)念愋秃螅捎酶奖愕姆绞饺〉脭?shù)據(jù)。但是在調(diào)用該方法前,最好使用instanceof驗證對象的類型,防止類產(chǎn)生異常。 對于JPEG資源,getContent()返回一個對象,該對象實現(xiàn)了java.awt.Image.ImageProducer接口。下面的代碼演示了使用如何getContent()。 查看一下getContent()方法的源代碼,你會找到openConnection().getContent()。URL的openConnection()方法返回一個java.net.URLConnection對象。URLConnection的方法反映了資源和連接的細節(jié)信息,使我們能編寫代碼訪問資源。 下面的URLDemo2代碼演示了openConnection(),以及如何調(diào)用URLConnection的方法。 程序5: URLDemo2.java class URLDemo2 { URL url = new URL (args [0]); // 返回代表某個資源的連接的新的特定協(xié)議對象的引用 // 進行連接 // 打印header的內(nèi)容 編譯URLDemo2后,在命令行輸入java URLDemo2 http://www.javajeff.com,輸出如下: 仔細看一下前面的輸出,會看到叫做Content-Type的東西。Content-Type識別了資源數(shù)據(jù)的類型是text/html。text部分叫做類型,html部分叫做子類型。如果內(nèi)容是普通的文本,Content-Type的值可能是text/plain。text/html表明內(nèi)容是文本的但是html格式的。
URI uri = new URI ("
如果知道URI是有效的,不會產(chǎn)生URISyntaxException,可以使用靜態(tài)的create(String uri)方法。這個方法分解uri,如果沒有違反語法規(guī)則就建立URI對象,否則將捕捉到一個內(nèi)部URISyntaxException,并把該對象包裝在一個IllegalArgumentException中拋出。
下面的代碼片斷演示了create(String uri):
URI uri = URI.create ("
URI構(gòu)造函數(shù)和create(String uri)方法試圖分解出URI的authority的用戶信息、主機和端口部分。對于正確形式的字符串會成功,對于錯誤形式的字符串,他們將會失敗。如果想確認某個URI的authority是基于服務(wù)器的,并且能分解出用戶信息、主機和端口,這時候可以調(diào)用URI的parseServerAuthority()方法。如果成功分解出URI,該方法將返回包含用戶信息、主機和端口部分的新URI對象,否則該方法將產(chǎn)生一個URISyntaxException。
// 下面的parseServerAuthority()調(diào)用出現(xiàn)后會發(fā)生什么情況?
URI uri = new URI ("http://foo:bar").parseServerAuthority();
一旦擁有了URI對象,你就可以通過調(diào)用getAuthority()、getFragment()、getHost()、getPath()、getPort()、getQuery()、getScheme()、getSchemeSpecificPart()和 getUserInfo()方法提取信息。以及isAbsolute()、isOpaque()等方法。
import java.net.*;
public static void main (String [] args) throws Exception {
if (args.length != 1) {
System.err.println ("usage: java URIDemo1 uri");
return;
}
URI uri = new URI (args [0]);
System.out.println ("Fragment = " +uri.getFragment ());
System.out.println ("Host = " +uri.getHost ());
System.out.println ("Path = " +uri.getPath ());
System.out.println ("Port = " +uri.getPort ());
System.out.println ("Query = " +uri.getQuery ());
System.out.println ("Scheme = " +uri.getScheme ());
System.out.println ("Scheme-specific part = " + uri.getSchemeSpecificPart ());
System.out.println ("User Info = " +uri.getUserInfo ());
System.out.println ("URI is absolute: " +uri.isAbsolute ());
System.out.println ("URI is opaque: " +uri.isOpaque ());
}
}
輸入java URIDemo1命令后,輸出結(jié)果如下:
query://jeff@books.com:9000/public/manuals/appliances?stove#ge
Authority = jeff@books.com:9000
Fragment = ge
Host = books.com
Path = /public/manuals/appliances
Port = 9000
Query = stove
Scheme = query
Scheme-specific part = //jeff@books.com:9000/public/manuals/appliances?stove
User Info = jeff
URI is absolute: true
URI is opaque: false
import java.net.*;
public static void main (String [] args) throws Exception {
if (args.length != 1) {
System.err.println ("usage: java URIDemo2 uri");
return;
}
URI uri = new URI (args [0]);
System.out.println ("Normalized URI = " + uri.normalize());
}
}
在命令行輸入java URIDemo2 x/y/../z/./q,將看到下面的輸出:
Normalized URI = x/z/q
URI通過提供resolve(String uri)、resolve(URI uri)和relativize(URI uri)方法支持反向解析和相對化操作。如果指定的URI違反了RFC 2396語法規(guī)則,resolve(String uri)通過的內(nèi)部的create(String uri)調(diào)用間接地產(chǎn)生一個IllegalArgumentException。下面的代碼演示了resolve(String uri)和relativize(URI uri)。
import java.net.*;
public static void main (String [] args) throws Exception {
if (args.length != 2) {
System.err.println ("usage: " + "java URIDemo3 uriBase uriRelative");
return;
}
System.out.println ("Base URI = " +uriBase);
System.out.println ("Relative URI = " +uriRelative);
System.out.println ("Resolved URI = " +uriResolved);
System.out.println ("Relativized URI = " +uriRelativized);
}
}
編譯URIDemo3后,在命令行輸入java URIDemo3 http://www.somedomain.com/ x/../y,輸出如下:
Base URI = http://www.somedomain.com/
Relative URI = x/../y
Resolved URI = http://www.somedomain.com/y
Relativized URI = y
使用URL
下面的代碼片斷演示了使用URL(String url)建立一個URL對象,該對象封裝了一個簡單的URL組件和http協(xié)議處理程序。
URL url = new URL ("
一旦擁有了URL對象,就可以使用getAuthority()、getDefaultPort()、 getFile()、 getHost()、 getPath()、getPort()、 getProtocol()、getQuery()、getRef()、getUserInfo()、getDefaultPort()等方法提取各種組件。如果URL中沒有指定端口,getDefaultPort()方法返回URL對象的協(xié)議默認端口。getFile()方法返回路徑和查詢組件的結(jié)合體。getProtocol()方法返回資源的連接類型(例如http、mailto、ftp)。getRef()方法返回URL的片斷。最后,getUserInfo()方法返回Authority的用戶信息部分。還可以調(diào)用openStream()方法得到j(luò)ava.io.InputStream引用。使用這種引用,可以用面向字節(jié)的方式讀取資源。
下面是URLDemo1的代碼。該程序建立一個URL對象,調(diào)用URL的各種方法來檢索該URL的信息,調(diào)用URL的openStream()方法打開與資源的連接并讀取/打印這些字節(jié)。
import java.io.*;
import java.net.*;
public static void main (String [] args) throws IOException {
if (args.length != 1) {
System.err.println ("usage: java URLDemo1 url");
return;
}
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 ());
while ((ch = is.read ()) != -1) {
System.out.print ((char) ch);
}
is.close ();
}
}
在命令行輸入java URLDemo1 http://www.javajeff.com/articles/articles/html后,上面的代碼的輸出如下:
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
…
InputStream is = url.openStream ();
BufferedReader br = new BufferedReader (new InputStreamReader (is));
String line;
while ((line = br.readLine ()) != null) {
System.out.println (line);
}
is.close ();
有時候按字節(jié)的次序讀取數(shù)據(jù)并不方便。例如,如果資源是JPEG文件,那么獲取一個圖像處理過程并向該過程注冊一個用戶使用數(shù)據(jù)的方法更好。如果出現(xiàn)這種情況,你就有必要使用getContent()方法。
URL url = new URL (args [0]);
Object o = url.getContent ();
if (o instanceof ImageProducer) {
ImageProducer ip = (ImageProducer) o;
// ...
}
import java.io.*;
import java.net.*;
import java.util.*;
public static void main (String [] args) throws IOException {
if (args.length != 1) {
System.err.println ("usage: java URLDemo2 url");
return;
}
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 ());
}
}
URLConnection的getHeaderFields()方法返回一個java.util.Map。該map包含header名稱和值的集合。header是基于文本的名稱/值對,它識別資源數(shù)據(jù)的類型、數(shù)據(jù)的長度等等。
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
Content-Type是多用途Internet郵件擴展(MIME)的一部分。MIME是傳統(tǒng)的傳輸消息的7位ASCII標準的一種擴展。通過引入了多種header,MIME使視頻、聲音、圖像、不同字符集的文本與7位ASCII結(jié)合起來。當(dāng)使用URLConnection類的時候,你會遇到getContentType()和getContentLength()。這些方法返回的值是Content-Type和Content