URI
URI = Uniform Resource Identifier
There are two types of URIs: URLs andURNs.
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
是彼此关联的。
URI
位于顶层,
URL
和
URN
的范畴位于底层。
URL
和
URN
都是
URI
的子范畴。
URI
翻译为统一资源标识,它是以某种标准化的方式标识资源的字符串。这种字符串以
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
看做是以某种方式引用某种资源,而对环境没有依赖。若是使用文件系统做类比,绝对
URI
相似于从根目录开始的某个文件的路径。相对
URI
不以
scheme
开始,一个例子是
articles/articles.html
。能够把相对
URI
看做是以某种方式引用某种资源,而这种方式依赖于标识符出现的环境。若是用文件系统做类比,相对
URI
相似于从当前目录开始的文件路径。
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]
可选的受权机构(
authority
)标识了该
URI
名字空间的命名机构。若是有这一部分则以‘
//
’开始。它能够是基于服务器或基于受权机构的。基于受权机构有特定的语法(本文没有讨论,由于不多使用它),而基于服务器的语法以下:
[userinfo@] host [:port]
基于服务器的
authority
以用户信息(例如用户名)开始,后面跟着一个
@
符号,紧接着是主机的名称,以及冒号和端口号。例如
jeff@x.com:90
就是一个基于服务器的
authority
,其中
jeff
为用户信息,
x.com
为主机,
90
为端口。
可选的
path
根据
authority
(若是提供了)或
schema
(若是没有
authority
)定义资源的位置。路径(
path
)能够分红一系列的路径片段(
path segment
),每一个路径片段使用‘
/
’与其它片段隔开。若是第一个路径片段以‘
/
’开始,该路径就被认为是绝对的,不然路径就被认为是相对的。例如,
/a/b/c
由三个路径片段
a
、
b
和
c
组成,此外这个路径是绝对的,由于第一个路径片段(
a
)的前缀是‘
/
’。
可选的
query
定义要传递给资源的查询信息。资源使用该信息获取或生成其它的的数据传递回调用者。例如,
http://www.somesite.net/a?x=y, x=y
就是一个
query
,在这个查询中
x
是某种实体的名称,
y
是该实体的值。
最后一个部分是
fragment
。当使用
URI
进行某种检索操做时,后面执行操做的软件使用
fragment
聚焦于软件感兴趣的资源部分。
分析一个例子
ftp://george@x.com:90/public/notes?text=shakespeare#hamlet
上面的
URI
把
ftp
识别为
schema
,把
george@x.com:90
识别为基于服务器的
authority
(其中
george
是用户信息,
x.com
是主机,
90
是端口),把
/public/notes
识别为路径,把
text=shakespeare
识别为查询,把
hamlet
识别为片段。本质上它是一个叫作
george
的用户但愿经过
/public/notes
路径在服务器
x.com
的
90
端口上检索
shakespeare
文本的
hamlet
信息。
URI
的标准化(
normalize
)
标准化能够经过目录术语来理解。假定目录
x
直接位于根目录之下,
x
有子目录
a
和
b
,
b
有文件
memo.txt
,
a
是当前目录。为了显示
memo.txt
中的内容,你可能输入
type \x\.\b\memo.txt
。你也可能输入
type \x\a\..\b\memo.txt
,在这种状况下,
a
和
..
的出现是没有必要的。这两种形式都不是最简单的。可是若是输入
\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
将产生
x://a/b/c
。根据
x://a/
相对化(
Relative
)
x://a/b/c
将产生
b/c
。
URI
不能读取
/
写入资源,这是统一的资源定位器(
URL
)的任务。
URL
是一种
URI
,它的
schema
是已知的网络协议,而且它把
URI
与某种协议处理程序联系起来(一种与资源通信的读
/
写机制)。
URI
通常不能为资源提供持久不变的名称。这是统一的资源命名(
URN
)的任务。
URN
也是一种
URI
,可是全球惟一的、持久不便的,即便资源再也不存在或再也不使用。
使用
URI
Java
API
经过提供
URI
类(位于
java.net
包中),使咱们在代码中使用
URI
成为可能。
URI
的构造函数创建
URI
对象,而且分析
URI
字符串,提取
URI
组件。
URI
的方法提供了以下功能:
1
)决定
URI
对象的
URI
是绝对的仍是相对的;
2
)决定
URI
对象是
opaque
仍是
hierarchical
;
3
)比较两个
URI
对象;
4
)标准化(
normalize
)
URI
对象;
5
)根据
Base URI
解析某个
Relative URI
;
6
)根据
Base URI
计算某个
URI
的相对
URI
;
7
)把
URI
对象转换为
URL
对象。
在
URI
里面有多个构造函数,最简单的是
URI(String uri)
。这个构造函数把
String
类型的参数
URI
分解为组件,并把这些组件存储在新的
URI
对象中。若是
String
对象的
URI
违反了
RFC 2396
的语法规则,将会产生一个
java.net.URISyntaxException
。
下面的代码演示了使用
URI(String uri)
创建
URI
对象:
URI uri = new URI ("http://www.cnn.com");
|
若是知道
URI
是有效的,不会产生
URISyntaxException
,可使用静态的
create(String uri)
方法。这个方法分解
uri
,若是没有违反语法规则就创建
URI
对象,不然将捕捉到一个内部
URISyntaxException
,并把该对象包装在一个
IllegalArgumentException
中抛出。
下面的代码片段演示了
create(String uri)
:
URI uri = URI.create ("http://www.cnn.com");
|
URI
构造函数和
create(String uri)
方法试图分解出
URI
的
authority
的用户信息、主机和端口部分。对于正确形式的字符串会成功,对于错误形式的字符串,他们将会失败。若是想确认某个
URI
的
authority
是基于服务器的,而且
能
分解出用户信息、主机和端口,这时候能够调用
URI
的
parseServerAuthority()
方法。若是成功分解出
URI
,该方法将返回包含用户信息、主机和端口部分的新
URI
对象,不然该方法将产生一个
URISyntaxException
。
下面的代码片段演示了
parseServerAuthority()
:
//
下面的
parseServerAuthority()
调用出现后会发生什么状况?
URI uri = new URI ("//foo:bar").parseServerAuthority();
|
一旦拥有了
URI
对象,你就能够经过调用
getAuthority()
、
getFragment()
、
getHost()
、
getPath()
、
getPort()
、
getQuery()
、
getScheme()
、
getSchemeSpecificPart()
和
getUserInfo()
方法提取信息。以及
isAbsolute()
、
isOpaque()
等方法。
程序
1: URIDemo1.java
import java.net.*; public class URIDemo1 { 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 ("Authority = " +uri.getAuthority ()); 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
命令后,输出结果以下:
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
|
URI
类支持基本的操做,包括标准化(
normalize
)、分解(
resolution
)和相对化(
relativize
)。下例演示了
normalize()
方法。
程序
2: URIDemo2.java
import java.net.*; class URIDemo2 { 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
上面的输出显示
y
、
..
和
.
消失了。
URI
经过提供
resolve(String uri)
、
resolve(URI uri)
和
relativize(URI uri)
方法支持反向解析和相对化操做。若是指定的
URI
违反了
RFC 2396
语法规则,
resolve(String uri)
经过的内部的
create(String uri)
调用间接地产生一个
IllegalArgumentException
。
下面
的代码演示了
resolve(String uri)
和
relativize(URI uri)
。
程序
3: URIDemo3.java
import java.net.*; class URIDemo3 { public static void main (String [] args) throws Exception { if (args.length != 2) { System.err.println ("usage: " + "java URIDemo3 uriBase uriRelative"); return; } URI uriBase = new URI (args [0]); System.out.println ("Base URI = " +uriBase); URI uriRelative = new URI (args [1]); System.out.println ("Relative URI = " +uriRelative); URI uriResolved = uriBase.resolve (uriRelative); System.out.println ("Resolved URI = " +uriResolved); URI uriRelativized = uriBase.relativize (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
Java
提供了
URL
类,每个
URL
对象都封装了资源标识符和协议处理程序。得到
URL
对象的途径之一是调用
URI
的
toURL()
方法,也能够直接调用
URL
的构造函数来创建
URL
对象。
URL
类有多个构造函数。其中最简单的是
URL(String url)
,它有一个
String
类型的参数。若是某个
URL
没有包含协议处理程序或该
URL
的协议是未知的,其它的构造函数会产生一个
java.net.MalformedURLException
。
下面的代码片段演示了使用
URL(String url)
创建一个
URL
对象,该对象封装了一个简单的
URL
组件和
http
协议处理程序。
URL url = new URL ("http://www.informit.com");
|
一旦拥有了
URL
对象,就可使用
getAuthority()
、
getDefaultPort()
、
getFile()
、
getHost()
、
getPath()
、
getPort()
、
getProtocol()
、
getQuery()
、
getRef()
、
getUserInfo()
、
getDefaultPort()
等方法提取各类组件。若是
URL
中没有指定端口,
getDefaultPort()
方法返回
URL
对象的协议默认端口。
getFile()
方法返回路径和查询组件的结合体。
getProtocol()
方法返回资源的链接类型(例如
http
、
mailto
、
ftp
)。
getRef()
方法返回
URL
的片段。最后,
getUserInfo()
方法返回
Authority
的用户信息部分。还能够调用
openStream()
方法获得
java.io.InputStream
引用。使用这种引用,能够用面向字节的方式读取资源。
下面是
URLDemo1
的代码。该程序创建一个
URL
对象,调用
URL
的各类方法来检索该
URL
的信息,调用
URL
的
openStream()
方法打开与资源的链接并读取
/
打印这些字节。
程序
4: 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 (); } }
|
在命令行输入
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
…
|
URL
的
openStream()
方法返回的
InputStream
类型,这意味着你必须按字节次序读取资源数据,这种作法是恰当的,由于你不知道将要读取的数据是什么类型。若是你事先知道要读取的数据是文本,而且每一行以换行符(
\n
)结束,你就能够按行读取而不是按字节读取数据了。
下面的代码片段演示了把一个
InputStream
对象包装进
InputStreamReader
以从
8
位过渡到
16
位字符,进而把结果对象包装进
BufferedReader
以调用其
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()
方法时,它会返回某种对象的引用,而你能够调用该对象的方法(在转换成适当的类型后),采用更方便的方式取得数据。可是在调用该方法前,最好使用
instanceof
验证对象的类型,防止类产生异常。
对于
JPEG
资源,
getContent()
返回一个对象,该对象实现了
java.awt.Image.ImageProducer
接口。下面的代码演示了使用如何
getContent()
。
URL url = new URL (args [0]); Object o = url.getContent (); if (o instanceof ImageProducer) { ImageProducer ip = (ImageProducer) o; // ... }
|
查看一下
getContent()
方法的源代码,你会找到
openConnection().getContent()
。
URL
的
openConnection()
方法返回一个
java.net.URLConnection
对象。
URLConnection
的方法反映了资源和链接的细节信息,使咱们能编写代码访问资源。
下面
的
URLDemo2
代码演示了
openConnection()
,以及如何调用
URLConnection
的方法。
程序
5: 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 (); //
打印
header
的内容
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
是基于文本的名称
/
值对,它识别资源数据的类型、数据的长度等等。
编译
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
|
仔细看一下前面的输出,会看到叫作
Content-Type
的东西。
Content-Type
识别了资源数据的类型是
text/html
。
text
部分叫作类型,
html
部分叫作子类型。若是内容是普通的文本,
Content-Type
的值多是
text/plain
。
text/html
代表内容是文本的可是
html
格式的。
Content-Type
是多用途
Internet
邮件扩展(
MIME
)的一部分。
MIME
是传统的传输消息的
7
位
ASCII
标准的一种扩展。经过引入了多种
header
,
MIME
使视频、声音、图像、不一样字符集的文本与
7
位
ASCII
结合起来。当使用
URLConnection
类的时候,你会遇到
getContentType()
和
getContentLength()
。这些方法返回的值是
Content-Type
和
Content