1、URLConnection
首先URLConnection是一个抽象类,表示指向URL指定资源的活动连接,URLConnection有两个不同但相关的用途,首先,与URL类相比,它对服务器(特别是HTTP服务器)的交互提供了更多的控制。URLConnection可以检查服务器发送的首部,并相应地做出响应。它可以设置客户端请求中使用的首部字段。最后,URLConnection可以用POST、PUT和其他HTTP请求方法向服务器发回数据。其次URLConnection类是java的协议处理器机制的一部分,这个机制还包括URLStreamHandler类。协议处理器的思想很简单:它们将处理协议的细节和处理特定数据类型分开,提供相应的用户接口,并完成完整web浏览器所完成的其他操作。基类URLConnection是抽象类,要实现一个特定的协议,就要编写一个子类。这些子类可以在运行时由应用程序加载。
使用步骤(但并不一定执行所有这些步骤):
(1)构造一个URL对象。
(2)调用URL对象的openConnection()获取一个对应该URL的URLConnection对象。
(3)配置这个URLConnection。
(4)读取首部字段。
(5)获得输入流并读取数据。
(6)获得输出流并写入数据。
(7)关闭连接。
2、URLConnection 类仅有的一个构造函数为保护类型
protected URLConnection(URL url);
因此,除非派生该类来处理新的URL类型(即编写一个协议处理器),否则要通过调用URL类的openConnection()方法来创建这样一个对象。URLConnection类声明为抽象类。不过,除了一个方法外,其余方法都已经实现。覆盖这个类的其他方法很方便或者可能很有必要。必须由子类实现的这个方法是connect(),它建立与服务器的连接,因而依赖于服务器类型(HTTP,FTP等)。例如,sun.net.www.protocol.file.FileURLConnection的connect()方法将URL转换为适当目录中的一个文件名,创建该文件的MIME信息,然后打开一个指向该文件的缓冲FileInputStream。sun.net.www.protocol.http.HttpURLConnection的connect()方法会创建一个sun.net.www.http.HttpClient()对象,由它负责连接服务器。这里采用的是策略模式:URLConnection是一个抽象策略类,包含一个抽象方法connect(),其具体策略实现类主要有FileURLConnection类、HttpURLConnection类。URL为上下文类,保持一个URLConnection类的引用,通过openConnection()得到URLConnection类的对象。
3、读取服务器的数据下面是使用URLConnection 对象从一个URL获取数据所需的最起码的步骤:
(1)构造一个URL对象
(2)调用这个对象的openConnection()方法,获取对应的URLConnection对象
(3)调用这个URLConnection 的getInputStream()方法
(4)使用通常的流API读取输入数据
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
//从服务器读取数据
public class SourceViewer2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
if(args.length > 0){
try{
//打开URLConection进行读取
URL u = new URL(args[0]);
URLConnection uc = u.openConnection();
try(InputStream raw = uc.getInputStream()){//自动关闭
InputStream buffer = new BufferedInputStream(raw);
//将InputStreat串联到一个Reader
Reader reader = new InputStreamReader(buffer);
int c;
while((c = reader.read()) != -1){
System.out.println((char)c);
}
}
}catch(MalformedURLException ex){
System.out.println(args[0] + " is not a parseable URL");
}catch(IOException ex){
System.out.println(ex);
}
}
}
}
对于这个例子中这样一个简单的输入流,RUL和URLConnection之间的区别并不明显。这两个类的最大不同在于:URLConnection提供了对HTTP首部的访问;URLConnection可以配置发送给服务器的请求参数;URLConnection除了读取服务器数据外,还可以向服务器写数据。
4、读取指定的首部的字段(读取服务器发送给客户端的首部信息)
Content-type: public String getContentType(); 该方法返回响应主题的MIME内容型,eg:Content-type text/html;charset=UTF-8;
Content-length: public int getContentLength()(public int getContentlengthLong()返回一个long类型); 该方法主要表明内容共有多少个字节 ;
从http连接读取二进制文件的一般过程:
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class BinarySaver {
public static void main(String[] args) {
for(int i = 0; i < args.length; i++){
try{
URL root = new URL(args[i]);
saveBinaryFile(root);
}catch(MalformedURLException ex){
System.err.println(args[i] + " is not URL I understand.");
}catch (IOException ex) {
System.err.println(ex);
}
}
}
public static void saveBinaryFile(URL u) throws IOException{
URLConnection uc = u.openConnection();
String contentType = uc.getContentType();
int contentLength = uc.getContentLength();
if(contentType.startsWith("text/") || contentLength == -1){
throw new IOException("This is not a binary file.");
}
try(InputStream raw = uc.getInputStream()){
InputStream in = new BufferedInputStream(raw);
byte[] data = new byte[contentLength];
int offset = 0;
while(offset < contentLength){
int bytesRead = in.read(data, offset, data.length - offset);
if(bytesRead == -1){
//读取文件末尾
break;
}
offset += bytesRead;
}
//注意读取文件是否中断
if(offset != contentLength){
throw new IOException("Only read " + offset
+ " bytes; Expected " + contentLength + "bytes");
}
String filename = u.getFile();
filename = filename.substring(filename.lastIndexOf("/") + 1);
try(FileOutputStream fout = new FileOutputStream(filename)){
fout.write(data);
fout.flush();
}
}
}
}
Content-encoding: public String getContentEncoding();该方法主要是指出内容是如何编码的,与字符编码不同;
Date: public long getDate(); 放回一个long类型,指出文档何时发送,距1970年1月1日子夜后过去了多少毫秒
Expires: public long getExpiration(); 有些文档有基于服务器的过期日期,指示应当何时从缓存中删除文档,并从服务器重新下载。
Last-modified: public long getLastModified(); 返回文档的最后修改日期
5、获取任意首部字段,仅仅对以上方法的封装
String getHeaderField(String name);返回指定首部字段的值,不要假定getHeaderField返回的值一定有效,必须进行检查确保它是否为null.
String getHeaderFieldKey(int n);//返回第n个首部字段的键。请求方法的本身是第0个首部,它的键为null。即第一个首部的编号为1.
String getHeaderField(int n);//返回第n个首部字段的值。在HTTP中,包含请求方法的路径和起始行是第0个首部字段,实际的首部字段编号为1.
long getHeaderFieldDate(String name,long default);//该方法首先获取由name参数指定的首部字段,然后尝试将这个字符串转换为一个long,指示从1970年1月1日子夜后经过的毫秒数。
int getHeaderFieldInt(String name,int default);//该方法获取首部name的值,尝试将其转换为int
6、缓存
Web浏览器的缓存默认情况下认为使用GET通过HTTP访问的页面可以缓存,也应当缓存。使用HTTPS或者POST访问的页面通常不缓存。HTTP可以针对首部信息作出如下调整进行缓存:
Expires首部:(主要针对HTTP1.0)指示可以缓存这个资源的表示,直到指定时间为止。
Cache-control首部(HTTP1.1)提供了细粒度的缓存策略:
----max-age=[seconds]:从现在直到缓存项过期之前的秒数。
----s-maxage=[seconds]:从现在起,直到缓存项在共享缓存中过期之前的秒数。私有缓存可以将缓存保存更长时间。
----public:可以缓存一个经过认证的响应。否则已认证的响应不能缓存。
----private:仅单个用户缓存可以保存响应,而共享缓存不应保存。
----no-cahce:这个策略的作用与名字不太一致。缓存项依然可以缓存,不过客户端在每次访问时要用一个Etag或Last-Modified首部重新验证响应的状态。
----no-store:无论如何不缓存。
如果Cache-control和Expires首部都出现,Cache-control会覆盖Expires。服务器可以在一个首部中发送多个Cache-control首部,只要它们没有冲突。
Last-modified首部指示资源最后修改的日期,客户端可以使用一个HEAD请求来检查这个日期,只有当本地缓存的副本早于Last-Modified日期时,它才会真正执行GET来获取资源。
Etag首部(HTTP1.1)是资源改变时这个资源的唯一标识。客户端可以使用一个HEAD请求来检查这个标识符,只有当本地缓存的副本有一个不同的Etag时,它才会真正的执行GET请求来获取资源。
根据这个信息,客户端可以充分加以利用,如果本地缓存中有这个资源的一个表示,而且还没有到它的过期时间,那么可以直接使用这个资源,无需与服务器交互。
如果本地缓存中有这个资源的一个表示,不过已经到它的过期时间,在完成完整的GET之前,可以检查服务器的HEAD首部,查看资源是否已经改变。
7、java的Web缓存
关于缓存的实现具体看这篇博客:http://blog.youkuaiyun.com/qq_25605779/article/details/70219717
8、配置连接
protected URL url;
protected boolean doInput = true;
protected boolean doOutput = false;
protected boolean allowUserInteraction = defaultAllowUserInteraction;
protected boolean useCaches = defaultUseCaches;
protected long ifModifiedSince = 0;
protected boolean connected = flase;
由于以上字段都是保护字段,所以他们的值要通过相应的设置方法和获取方法来访问和修改:get set方法。并且只能在URLConnection连接之前修改这些字段(试图从连接读取内容或首部之前)。对于设置字段的方法,如果调用这些方法时连接已经打开,大多数方法会抛出一个IllegalStateException异常。一般情况下,只能在连接打开前设置URLConnection对象的属性。
还有一些获取方法和设置方法定义了所有URLConnection实例的默认行为。它们包括:
public boolean getDefaultUseCaches()
public void setDefaultUseCaches(boolean defaultUseCaches)
public static void setDefaultAllowUserInteraction(boolean defaultAllowUserInteraction)
public static boolean getDefaultAllowUserInterfaction();
public static FileNameMap getFileNameMap()
public static void setFileNameMap(FileNameMap map)
这些方法可以在任何时候调用,新的默认值只应用于设置这些新默认值之后构造的URLConnection对象。
9、配置客户端请求HTTP首部(向服务器发送首部信息)
在打开服务器与客户端连接之前,使用setRequestProperty()方法为HTTP首部增加首部字段:
public void setRequestRoperty(String name, String value)
增加另外一个属性值,需要使用addRequestProperty()方法
public void addRequestProperty(String name, String value)
查看URLConnection中的首部
public String getRequestProperty(String name)
获取连接的所有请求的属性并作为一个Map返回:
public Map<String,List<String>> getRequestProperties()
10、向服务器写数据
有时需要向URLConnection写入数据,例如,使用post向web服务器提交表单,或者使用put上传文件。getOutputStream()方法返回一个OutputStream,可以用来写入数据给服务器:
public OutputStream getOutputStream()
由于在默认情况下不允许输出,所以在请求输出流之前必须调用setDoOutput(true)。
11、HttpURLConnection
HttpURLConnection类是URLConnection的一个抽象子类,它提供了另外一些方法,在处理http URL时尤其有帮助。具体地,它包含的方法可以获得和设置请求方法、确定是否重定向、获得响应码和消息,以及确定是否使用了代理服务器。等