Java核心技术·卷Ⅱ:高级特性(原书第10版)
上QQ阅读APP看书,第一时间看更新

4.4.2 使用URLConnection获取信息

如果想从某个Web资源获取更多信息,那么应该使用URLConnection类,通过它能够得到比基本的URL类更多的控制功能。

当操作一个URLConnection对象时,必须像下面这样非常小心地安排操作步骤:

1)调用URL类中的openConnection方法获得URLConnection对象:

2)使用以下方法来设置任意的请求属性:

我们将在本节的稍后部分以及API说明中讨论这些方法。

3)调用connect方法连接远程资源:

除了与服务器建立套接字连接外,该方法还可用于向服务器查询头信息(header information)。

4)与服务器建立连接后,你可以查询头信息。getHeaderFieldKey和getHeaderField这两个方法枚举了消息头的所有字段。getHeaderFields方法返回一个包含了消息头中所有字段的标准Map对象。为了方便使用,以下方法可以查询各标准字段:

5)最后,访问资源数据。使用getInputStream方法获取一个输入流用以读取信息(这个输入流与URL类中的openStream方法所返回的流相同)。另一个方法getContent在实际操作中并不是很有用。由标准内容类型(比如text/plain和image/gif)所返回的对象需要使用com.sun层次结构中的类来进行处理。也可以注册自己的内容处理器,但是在本书中我们不讨论这项技术。

警告:一些程序员在使用URLConnection类的过程中形成了错误的观念,他们认为URLConnection类中的getInputStream和getOutputStream方法与Socket类中的这些方法相似,但是这种想法并不十分正确。URLConnection类具有很多表象之下的神奇功能,尤其在处理请求和响应消息头时。正因为如此,严格遵循建立连接的每个步骤显得非常重要。

下面将详细介绍一下URLConnection类中的一些方法。有几个方法可以在与服务器建立连接之前设置连接属性,其中最重要的是setDoInput和setDoOutput。在默认情况下,建立的连接只产生从服务器读取信息的输入流,并不产生任何执行写操作的输出流。如果想获得输出流(例如,用于向一个Web服务器提交数据),那么你需要调用:

接下来,也许想设置某些请求头(request header)。请求头是与请求命令一起被发送到服务器的。例如:

setIfModifiedSince方法用于告诉连接你只对自某个特定日期以来被修改过的数据感兴趣;setUseCaches和setAllowUserInteraction这两个方法只作用于Applet;setUseCaches方法用于命令浏览器首先检查它的缓存;setAllowUserInteraction方法则用于在访问有密码保护的资源时弹出对话框,以便查询用户名和口令(见图4-7)。

最后我们再介绍一个总览全局的方法:setRequestProperty,它可以用来设置对特定协议起作用的任何“名-值(name/value)对”。关于HTTP请求头的格式,请参见RFC 2616,其中的某些参数没有很好地建档,它们通常在程序员之间口头传授。例如,如果你想访问一个有密码保护的Web页,那么就必须按如下步骤操作:

1)将用户名、冒号和密码以字符串形式连接在一起。

2)计算上一步骤所得字符串的Base64编码。(Base64编码用于将字节序列编码成可打印的ASCII字符序列。)

3)用"Authorization"这个名字和"Basic"+encoding的值调用setRequestProperty方法。

提示:我们上面介绍的是如何访问一个有密码保护的Web页。如果想要通过FTP访问一个有密码保护的文件时,则需要采用一种完全不同的方法:构建如下格式的URL:

图4-7 网络密码对话框

一旦调用了connect方法,就可以查询响应头信息了。首先,我们将介绍如何枚举所有响应头的字段。似乎是为了展示自己的个性,该类的实现者引入了另一种迭代协议。调用如下方法:

可以获得响应头的第n个键,其中n从1开始!如果n为0或大于消息头的字段总数,该方法将返回null值。没有哪种方法可以返回字段的数量,你必须反复调用getHeaderFieldKey方法直到返回null为止。同样地,调用以下方法:

可以得到第n个值。

getHeaderFields方法可以返回一个封装了响应头字段的Map对象。

下面是一组来自典型的HTTP请求的响应头字段。

为了简便起见,Java提供了6个方法用以访问最常用的消息头类型的值,并在需要的时候将它们转换成数字类型,这些方法的详细信息请参见表4-1。返回类型为long的方法返回的是从格林尼治时间1970年1月1日开始计算的秒数。

表4-1 用于访问响应头值的简便方法

通过程序清单4-6的程序,可以对URL连接做一些试验。程序运行起来后,请在命令行中输入一个URL以及用户名和密码(可选),例如:

该程序将输出以下内容:

·消息头中的所有键和值。

·表4-1中6个简便方法的返回值。

·被请求资源的前10行信息。

程序清单4-6 urlConnection/URLConnectionTest.java

java.net.URL 1.0

·InputStream openStream()

打开一个用于读取资源数据的输入流。

·URLConnection openConnection();

返回一个URLConnection对象,该对象负责管理与资源之间的连接。

java.net.URLConnection 1.0

·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 getAllowUserInteraction()

如果allowUserInteraction为true,那么可以查询用户的口令。请注意,URLConnection本身并不提供这种查询功能。查询必须由浏览器或浏览器插件之类的外部程序实现。

·void setConnectTimeout(int timeout)5.0

·int getConnectTimeout()5.0

设置或得到连接超时时限(单位:毫秒)。如果在连接建立之前就已经达到了超时的时限,那么相关联的输入流的connect方法就会抛出一个SocketTimeoutException异常。

·void setReadTimeout(int timeout)5.0

·int getReadTimeout()5.0

设置读取数据的超时时限(单位:毫秒)。如果在一个读操作成功之前就已经达到了超时的时限,那么read方法就会抛出一个SocketTimeoutException异常。

·void setRequestProperty(String key,String value)

设置请求头的一个字段。

·Map<String,List<String>>getRequestProperties()1.4

返回请求头属性的一个映射表。相同的键对应的所有值被放置在同一个列表中。

·void connect()

连接远程资源并获取响应头信息。

·Map<String,List<String>>getHeaderFields()1.4

返回响应的一个映射表。相同的键对应的所有值被放置在同一个列表中。

·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编码机制并不是用Content-Encoding头来设定的。

·long getDate()

·long getExpiration()

·long getLastModified()

获取创建日期、过期日以及最后一次被修改的日期。这些日期指的是从格林尼治时间1970年1月1日午夜开始计算的秒数。

·InputStream getInputStream()

·OutputStream getOutputStream()

返回从资源读取信息或向资源写入信息的流。

·Object getContent()

选择适当的内容处理器,以便读取资源数据并将它转换成对象。该方法对于读取诸如text/plain或image/gif之类的标准内容类型并没有什么用处,除非你安装了自己的内容处理器。