URL和URI
URL和URLConnection类封装了大量复杂的实现细节,这些细节涉及如何从远程站点获取信息。例如,可以通过传递字符串来构建一个URL对象 :
URL url = new URL(String);
如果只是想获得该资源的内容,可以使用
URL
类中的
openStream
方法。该方法返回一个
InputStream
对象,然后就可以按照一般的用法来使用这个对象了,比如用它构建一个
Scanner
对象。
java.net包对统一资源定位符(uniform resource locator,URL)和统一资源标识符( uniform resource identifier,URI)作了非常有用的区分。URI是个纯粹的句法结构,用于指定标识Web资源的字符串的各个不同部分。URL是URI的一个特例,它包含了用于定位Web资源的足够信息 。
在Java类库中,URI类不包含任何用于访问资源的方法,它的惟一作用就是解析。相反的是,URL类可以打开一个到达资源的流。因此,URL类只能作用于那些Java类库知道该如何处理的模式,例如http:、https:、 ftp:、本地文件系统(file:)和JAR文件(jar:)。
URI规范给出了标记这些标识符的规则 。一个URI具有以下句法 :
[scheme:]schemeSpecificPart[#fragment]
[ . . . ]
表示可选部分,它与:和
#
可以被包含在标识符内 .
包含scheme:部分的URI被称为绝对URI。否则,被称为相对URI。如果绝对URI的schemeSpecificPart不是以/开头的,我们就称它是不透明的 。所有绝对的透明URI和所有相对URI都是分层的(hierarchical)。
一个分层URI的schemeSpecificPart具有以下结构
[//authority][path][?query]
对于那些基于服务器的
URI
,
authority
部分采用以下形式
[user-info@]host[:port]
URI
类的作用之一是解析标识符并将它分解成各种不同的组成部分 。
URI
类的另一个作用是处理绝对标识符和相对标识符。
http://xxx/a/b/c.html
和
../a/b/c.html#xxx可以合为http://xxx/a/b/c.html#xxx这个过程被称为相对URL的转换( resolving)。
与此相反的过程称为相对化(
relativization
)。
使用URLConnection获取信息
如果想从某个Web资源获取更多信息,那么应该使用URLConnection类,它能得到比基本的URL类更多的控制功能。
1.调用URL类中的openConnection方法获得URLConnection对象
URLConnection uc = url.openConnection();
2.
设置任意的请求属性
3.
调用
connect
方法连接远程资源
uc.connect();
4.
与服务器建立连接后,你可以查询头信息。
getHeaderFieldKey
和
getHeaderField
两个方法列举了消息头的所有字段
getHeaderFields
方法返回一个包含了消息头中所有字段的标准
Map
对象。
5. 最后,访问资源数据。使用getInputStream方法获取一个输入流用以读取信息(这个输入流与URL类中的openStream方法所返回的流相同)。另一个方法getContent在实际操作中并不是很有用。
在默认情况下建立的连接只有从服务器读取信息的输入流,并没有任何执行写操作的输出流。如果想获得输出流(例如,
向一个Web服务器提交数据),那么你需要调用 uc.setDoOutput(true);接下来,也许想设置某些请求头(request header)。请求头是与请求命令一起被发送到服务器的。
setIfModifiedSince方法用于告诉连接你只对自某个特定日期以来被修改过的数据感兴趣;setUseCaches和setAllowUserInteraction这两个方法只作用于Applet;setUseCaches方法用于命令浏览器首先检查它的缓存;setAllowUserInteraction方法则用于在访问有密码保护的资源时弹出对话框。
setRequestProperty,它可以用来设置对特定协议起作用的任何“名-值(name/value)对”。例如,如果你想访问一个有口令保护的Web页
1.将用户名、冒号和口令以字符串形式连接在一起
String string=username+":"+password;
2.
计算上一步骤所得字符串的
base64
编码。(
base64
编码用于将字节流编码成可打印的
ASCII
字符流。)
String code =base64Encode(string);
3.
调用
setRequestProperty
方法,设置
name
参数的值为“
Authorization
”、
value
参数的值为“
Basic
”
+encoding .
uc.setRequestProperty("Authorization","Basic"+code);
一旦调用了
connect
方法,就可以查询响应头信息。首先,我们将介绍如何列举所有响应头的字段。似乎是为了展示自己的个性,该操作采用了另一种迭代方式。
String key = uc.getHeaderFieldKey(n);
可以获得响应头的第
n
个键,其中
n
从
1
开始。如果
n
为
0
或大于消息头的字段总数,该方法将返回
null
值。没有哪种方法可以返回字段的数量,你必须反复调用
getHeaderFieldKey
方法直到返回
null
为止。
String value=uc.getHeaderField(n);
getHeaderFields
方法可以返回一个封装了响应头字段的
Map
对象 。
Map<String,List<String>> map = uc.getHeaderFields();
为了简便起见,
Java
提供了
6
个方法用以访问大多数常用的消息头类型的值,并在需要的时候将它们转换成数字类型。
返回类型为
long
的方法返回的是从格林尼治时间
1970
年
1
月
1
日开始计算的秒数。
Date getdate long
Expires getExpiration long
Last-Modified getLastModified long
Content-Length getContentLength int
Content-Type getContentType String
Content-Encoding getContentEncoding String
除了
base64
编码的计算稍显复杂之外,程序的其他部分非常简单明了。有一个类
sun.misc.BASE64Encoder,
虽然没有被归档,但是可以用它来代替我们在示例程序中提供的类。
URL url = new URL("http://www.baidu.com");
URLConnection connection = url.openConnection();
connection.connect();
Map<String,List<String>>map = connection.getHeaderFields();
for(Map.Entry<String, List<String>> a:map.entrySet()){
String b = a.getKey();
for(String c:a.getValue()){
System.out.println(b+":"+c);
}
}
System.out.println(connection.getDate());
System.out.println(connection.getContentLength());
System.out.println(connection.getContentEncoding());
System.out.println(connection.getLastModified());
System.out.println(connection.getContentType());
System.out.println(connection.getExpiration());
Scanner scanner = new Scanner(connection.getInputStream());
while(scanner.hasNextLine()){
System.out.println(scanner.nextLine());
}
URLEncoder
static String encode(String s, String encoding)采用指定的字符编码模式(推荐使用“ UTF-8”)对字符串s进行编码,并返回它的URL
编码形式。在URL编码中, 'A' - 'Z', 'a' - 'z', '0' - '9', '-', '_', '.'和'*'等字符保持不变,空格被编码成'+',所有其他字符被编码成"%XY"形式的字节序列,其
中0xXY为该字节十六进制数
URLDecoder
static string decode(String s, String encoding) 采用指定编码模式对已编码字符串s进行解码,并返回结果。
URLConnection
void setDoInput(boolean doInput)
boolean getDoInput()
如果doInput为true,那么用户可以接收来自该URLConnection的输入。
void setDoOutput(boolean doOutput)
boolean getDoOutput()
如果doOutput为true,那么用户可以将输出发送到该URLConnection。
void setIfModifiedSince(long time)
long getIfModifiedSince()
属性ifModifiedSince用于配置URLConnection对象,使它只获取那些自从某个给定时间以来被修改过的数据。调用方法时需要传入的time参数指的是从格林尼治时间1970年1月
1日午夜开始计算的秒数。
void setUseCaches(boolean useCaches)
boolean getUseCaches()
如果useCaches为true,那么数据可以从本地缓存中得到。请注意, URLConnection本身并不维护这样一个缓存,缓存必须由浏览器之类的外部程序提供。
void setAllowUserInteraction(boolean allowUserInteraction)
boolean getAllowsUserInteraction()
如果allowUserInteraction为true,那么可以查询用户的口令。请注意,URLConnection本身并不提供这种查询功能。查询必须由浏览器或浏览器插件之类的外
部程序实现。
void setConnectTimeout(int timeout)
int getConnectTimeout()
设置或得到连接超时时限(单位:毫秒)。如果在连接建立之前就已经达到了超时的时限,那么输入流的connect方法就会抛出一个SocketTimeoutException异常。
void setReadTimeout(int timeout)
int getReadTimeout() 5.0
设置读取数据的超时时限(单位:毫秒)。如果在一个读操作成功之前就已经达到了超时的时限,那么read方法就会抛出一个SocketTimeoutException异常。
void setRequestProperty(String key, String value)
设置请求头的一个字段。
Map<String,List<String>> getRequestProperties()
返回请求头属性的一个映射表。相同的键对应的所有值被放置在同一个映射表中。
void connect()
连接远程资源并获取响应头信息。
Map<String,List<String>> Map getHeaderFields()
返回响应的一个映射表。相同的键对应的所有值被放置在同一个映射表中。
String getHeaderFieldKey(int n)
得到响应头第n个字段的键。如果n等于0或大于响应头字段的总数,该方法返回null值。
String getHeaderField(int n)
得到响应头第n个字段的值。如果n等于0或大于响应头字段的总数,该方法返回null值。
int getContentLength()
如果知道内容长度,则返回该长度值,否则返回-1。
String getContentType
获取内容的类型,比如text/plain或image/gif。
String getContentEncoding()
获取内容的编码,比如gzip。这个值不太常用,因为默认的identity编码并不是用ContentEncoding头来设定的。
long getDate()
long getExpiration()
long getLastModifed()
获取创建日期、过期日以及最后一次被修改的日期。这些日期指的是从格林尼治时间1970年1月1日午夜开始计算的秒数。
InputStream getInputStream()
OutputStream getOutputStream()
返回从资源读取信息或向资源写入信息的流。
Object getContent()
选择适当的内容处理器,以便读取资源数据并将它转换成对象。该方法不能用于读取诸如text/plain或image/gif之类的标准内容类型,除非你安装了自己的内容处理器。