文章目录
- 1. 简介
- 2. 打开URLConnection
- 3. 读取服务器的数据
- 4. 读取首部
- 5. 获取任意首部字段
1. 简介
URLConnection是一个抽象类,表示指向URL指定资源的活动连接。URLConnection有两个不同但相关的用途。首先,与URL类相比,它对服务器(特别是HTTP服务器)的交互提供了更多控制。URLConnection可以检查服务器发送的首部,并相应地做出响应。它可以设置客户端请求中使用的首部字段,最后URLConnection可以用POST、PUT和其他HTTP请求方法向服务器发回数据。其次URLConnection类是JAVA的协议处理器机制的一部分,这个机制还包括URLStreamHandler类。协议处理器的思想很简单:它们将处理协议的细节与处理特定数据类型分开,提供相应的用户接口,并完成完整的Web浏览器所完成的其他操作。基类java.net.URLConnection是抽象类,要实现一个特定的协议,就要编写一个有奇怪模式的URL,它不会“绝望”地发出一个错误信息,而是会为这个未知协议下载一个协议处理器,用它与服务器通信。java.net包中只有抽象的URLConnection类。具体的子类都隐藏在sun.net包中。URLConnection的许多方法以及一个构造函数都是保护的,意味着它们只能有URLConnection类或其子类实例访问。
2. 打开URLConnection
直接打开URLConnetion类的程序遵循一下基本步骤:
- 构造一个URL对象
- 调用这个URL对象的openConnetion()获取对应URL的URLConnetion对象
- 配置这个URLConnection
- 读取首部字段
- 获得输入流并读取数据
- 获得输出流写入数据
- 关闭连接
URLConnection类声明为抽象类,不过除了Connect方法,其他方法基本上它本身都已经实现了,所以其子类必须实现connect方法,该方法建立与服务器的连接,因而依赖与服务类型。第一次构造URLConnection时,它是未连接的,也就是说,本地和远程主机无法发送和接受数据。没有Socket连接这两个主机,connect()方法在本地和远程主机之间建立一个连接(一般使用TCP Socket),这样就可以收发数据了。不过我们一般不用显示的调用connect()方法,因为对于getInputStream()、getContent()、getHeaderField()方法和其他要求打开连接的方法,如果连接没有打开,这些方法内部会调用connect()方法
3. 读取服务器的数据
下面是使用URLConnection对象,从一个URL获取数据所需的最起码的步骤:
- 构造一个URL对象
- 调用这个URL对象的openConnection()方法,获取对应URL得URLConnection对象
- 调用这个URLConnection的getInputStream()方法
- 使用通常的流API读取输入流
下面使用URLConnection下载百度主页
public class QuizCardBuilder {
public static void main(String[] args) {
try{
//定义URL对象
URL u=new URL("http://www.baidu.com");
//获取URLConnection对象
URLConnection urlConnection=u.openConnection();
//在try()中写入我们需要自动关闭的资源,后期会帮我们自动关闭
try (InputStream raw=urlConnection.getInputStream()){
//以流链的形式封装原始流,加入缓存提高IO效率
InputStream buffer=new BufferedInputStream(raw);
Reader reader=new InputStreamReader(buffer);
int c;
while((c=reader.read())!=-1){
System.out.print((char)c);
}
}
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
对于这个例子这样一个简单的输入流,URL和URLConnection之间的区别并不明显
- URLConnection提供了对HTTP首部的访问
- URLConnection可以配置发送给服务器的请求参数
- URLConnection除了读取服务器数据外,还可以向服务器写入数据
4. 读取首部
如下图我们在谷歌浏览器中看到我们请求的头信息,其主要包括六个部分:
- Content-type:实际返回的内容的内容类型
- Content-length:实际返回的内容的内容长度
- Content-encoding:实际返回的内容的内容编码
- Date:实际返回的内容
- Last-modified:上一次在服务端的修改时间
- Expires:头信息过期时间
下面介绍URLConnection的对应获取方法
public String getContentType()
该方法返回响应体的MIME内容类型,它一路web服务器发来的一个有效的内容类型。
public class QuizCardBuilder {
public static void main(String[] args) {
try{
//定义URL对象
URL u=new URL("http://www.baidu.com");
//获取URLConnection对象
URLConnection urlConnection=u.openConnection();
//在try()中写入我们需要自动关闭的资源,后期会帮我们自动关闭
System.out.println(urlConnection.getContentType());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public int getContentLength()
告诉你内容有多少字节,如果没有Content-length首部就会返回-1.如果发现资源的长度超过Integer.MAXVALUE也会返回-1,下面代码实现了下载二进制文件。
public class QuizCardBuilder {
public static void main(String[] args) {
try {
//定义URL对象
URL u = new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fbbe9c79f-2f81-4387-bc64-c88b5daf78fa%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687610199&t=d3fd58ff3a0f868d2511c8fc1adaecbd");
URLConnection urlConnection = u.openConnection();
File mygram = new File("my.jpg");
saveBinaryFile(u);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void saveBinaryFile(URL u) throws IOException{
//获得URLConnection对象
URLConnection urlConnection=u.openConnection();
//获得Content-type
String contentype=urlConnection.getContentType();
System.out.println("Contentype:"+contentype);
//获得Content-length
int contetnLength=urlConnection.getContentLength();
System.out.println("ContentLength:"+contetnLength);
if(contentype.startsWith("text/")||contetnLength==-1){
throw new IOException("this is not a binary file");
}
//获得内容的IO流
try(InputStream raw=urlConnection.getInputStream()){
InputStream in =new BufferedInputStream(raw);
byte[] data=new byte[contetnLength];
int offset=0;
while(offset < contetnLength){
int bytesRead=in.read(data,offset,data.length-offset);
if(bytesRead==-1) break;
offset+=bytesRead;
}
try(FileOutputStream fout=new FileOutputStream("myimg.jpg")){
fout.write(data);
fout.flush();
}
}
}
}
百度图片的信息
成功下载到本地
public String getContentEncoding()
该方法返回一个String,指出内容是如何编码的,如果发送的内容没有编码,这个方法就会返回null,而不抛出异常。
public long getDate
该方法返回一个long,指出文档何时发送,这个时间按格林乔治标准时间(GMT)
public long getExpiration()
有些文档基于服务器的过期时间,指示应当何时从缓存中删除文档,并从服务器重新下载。
public long getLastModified()
返回文档的最后修改日期。
5. 获取任意首部字段
上面的6个方法请求首部中的指定字段,不过对于一个消息中包含多少首部字段理论上没有限制,下面5个方法可以检索首部中的任意字段。
public String getHeaderField(String name)
该方法返回指定首部字段的值,首部的名称不区分大小写,也不包含结束冒号。
public class QuizCardBuilder {
public static void main(String[] args) {
try {
//定义URL对象
URL u = new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fbbe9c79f-2f81-4387-bc64-c88b5daf78fa%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687610199&t=d3fd58ff3a0f868d2511c8fc1adaecbd");
URLConnection urlConnection = u.openConnection();
System.out.println("ContenType:"+urlConnection.getHeaderField("Content-Type"));
System.out.println("Date:"+urlConnection.getHeaderField("Date"));
System.out.println("Last-Modified+"+urlConnection.getHeaderField("Last-Modified"));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public String getHeaderFieldKey(int n)
这个方法返回第n个首部字段的键。请求方法本身就是第0个首部,它的键是null
public class QuizCardBuilder {
public static void main(String[] args) {
try {
//定义URL对象
URL u = new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fbbe9c79f-2f81-4387-bc64-c88b5daf78fa%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687610199&t=d3fd58ff3a0f868d2511c8fc1adaecbd");
URLConnection urlConnection = u.openConnection();
System.out.println("1:"+urlConnection.getHeaderFieldKey(1));
System.out.println("2:"+urlConnection.getHeaderFieldKey(2));
System.out.println("3:"+urlConnection.getHeaderFieldKey(3));
System.out.println("4:"+urlConnection.getHeaderFieldKey(4));
System.out.println("5:"+urlConnection.getHeaderFieldKey(5));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public String getHeaderField(int n)
这个方法是返回第n个首部的值。在HTTP中,包含请求方法和路径的起始行是第0个首部。下面代码来显示所有首部:
public class QuizCardBuilder {
public static void main(String[] args) {
try {
//定义URL对象
URL u = new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fbbe9c79f-2f81-4387-bc64-c88b5daf78fa%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687610199&t=d3fd58ff3a0f868d2511c8fc1adaecbd");
URLConnection urlConnection = u.openConnection();
for (int i = 1; ; i++) {
String header=urlConnection.getHeaderField(i);
if(header==null)break;
System.out.println(urlConnection.getHeaderFieldKey(i)+": "+header);
}
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public long getHeaderFieldDate(String name,long default)
这个方法首先获取由name参数指定的首部字段,然后尝试将这个字符转换为一个long(GMT 1970年1月1日子夜之后经过的毫秒数)。它可以用来获取表示日期的首部字段。为了将字符串转换为整数,该方法使用了java.util.Date中的parseDate方法
public class QuizCardBuilder {
public static void main(String[] args) {
try {
//定义URL对象
URL u = new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fbbe9c79f-2f81-4387-bc64-c88b5daf78fa%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687610199&t=d3fd58ff3a0f868d2511c8fc1adaecbd");
URLConnection urlConnection = u.openConnection();
System.out.println(urlConnection.getHeaderFieldDate("Expires",0));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public int getHeaderField(String name,int default)
这个方法获取首部字段name的值,尝试将其转换为int。
public class QuizCardBuilder {
public static void main(String[] args) {
try {
//定义URL对象
URL u = new URL("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fbbe9c79f-2f81-4387-bc64-c88b5daf78fa%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687610199&t=d3fd58ff3a0f868d2511c8fc1adaecbd");
URLConnection urlConnection = u.openConnection();
System.out.println(urlConnection.getHeaderFieldInt("content-length",-1));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}