Java网络编程——7.URLConnection

URLConnection是一个抽象类,表示指向URL指定资源的活动连接。与URL类相比,它对服务器的交互提供了更多的控制。URLConnection类是Java协议处理器(protocol handler)机制的一部分,它们将处理协议的细节与处理特定数据类型分开,提供相应的用户接口,并完成完整web浏览器所完成的其他操作。

1、打开URLConnection

直接使用URLConnection类的程序遵循以下基本步骤:

  1. 构造一个URL对象。
  2. 调用这个URL对象的openConnection()获取一个对应该URL的URLConnection对象。
  3. 配置这个URLConnection。
  4. 读取首部字段。
  5. 获得输入流并读取数据。
  6. 获得输出流并写入数据。
  7. 关闭连接。

第一次构造URLConnection时,它是未连接的,也就是说本地和远程主机无法发送和接收数据。connect()方法在本地和远程主机之间建立一个连接(一般使用TCP socket,但也可能通过其他机制来建立)。不过对于getInputStream()、getContent()等方法,如果连接尚未打开,它们就会调用connect(),因此你很少需要直接调用connect()。

2、读取服务器的数据

下面是使用URLConnection对象从一个URL获取数据所需的最起码的步骤:

  1. 构造一个URL对象。
  2. 调用这个URL对象的openConnection()获取一个对应该URL的URLConnection对象。
  3. 调用这个URLConnection的getInputStream()方法
  4. 使用通常的流API读取输入流。

URL和URLConnection这两个类最大的不同在于:URLConnection提供了对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 saveBinaryFile(URL url) throws IOException {
        URLConnection urlConnection = url.openConnection();
        String contentType = urlConnection.getContentType();
        int contentLength = urlConnection.getContentLength();
        if (contentType.startsWith("text/") || contentLength == -1) {
            throw new IOException("这不是二进制文件");
        }
        try (InputStream raw = urlConnection.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("期望" + contentLength + "字节,实际" + offset + "字节");
            }

            String fileName = url.getFile();
            fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
            try (FileOutputStream fOut = new FileOutputStream(fileName)) {
                fOut.write(data);
                fOut.flush();
            }
        }

    }

    public static void main(String[] args) {
        try {
            URL url = new URL("http://money.innovatelife.net/download?type=money-phone.apk");
            saveBinaryFile(url);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

getContentType()返回响应主体的MIME内容类型,getContentLength()告诉你内容有多少字节,getContentEncoding()返回内容的编码,getDate()返回文档何时发送等等。

3、缓存

Web浏览器多年来一直在缓存页面和图片,如果一个logo在网站的每一个页面上重复出现,浏览器一般只会从远程服务器上加载一次,将它保存在缓存中,每次需要的时候会从缓存重新加载。一些HTTP首部(包括Expires和Cache-Control)可以控制缓存。

  • Expires首部(主要针对HTTP 1.0)指示可以缓存这个资源表示,直到指定的时候为止。
  • Cache-Control首部(HTTP 1.1)提供了细粒度的缓存策略(如果跟Expires都出现会覆盖Expires):
    • max-age=[seconds]:从现在直到缓存项过期之前的秒数。
    • s-maxage=[seconds]:从现在起,直到缓存项在共享缓存中过期之前的秒数,私有缓存可以将缓存项保存更长时间。
    • public:可以缓存一个经过认证的响应。否则已认证的响应不能缓存。
    • private:仅单个用户缓存可以保存响应,而共享缓存不应保存。
    • no-cache:这个策略的作用与名字不太一致,缓存项仍然可以缓存,不过客户端在每次访问时要用一个Etag或Last-modified首部重新验证响应的状态。
    • no-store:不管怎样都不缓存。
  • Last-modified首部指示资源最后一次修改的日期,客户端可以使用一个HEAD请求来检查这个日期,只有当本地缓存的副本早于Last-modified日期时,它才会真正执行GET来获取资源。
  • Etag首部(HTTP 1.1)是资源改变时这个资源的唯一标识符。

Java的Web缓存:要安装URL类使用的系统级缓存,需要有ResponseCache、CacheRequest、CacheResponse这些的具体子类。

4、配置连接

URLConnection类有7个保护实例字段,定义了客户端如何向服务器做出请求。例如,如果doOutPut为true,那么除了通过这个URLConnection读取数据外,还可以将数据写入到服务器。如果useCaches为false,连接会绕过所有本地缓存,重新从服务器下载文件。只能在URLConnection连接之前修改这些字段,对于设置字段的方法,如果调用这些方法时连接已经打开,大多数方法会抛出一个IllegalSateException。字段如下:

  • url:指定了这个URLConnection连接的URL。
  • doInput:URLConnection可以用来读取服务器为true,否则为false。
  • doOutput:URLConnection可以用于写入为true,否则为false。
  • allowUserInteraction:是否允许用户交互。
  • useCaches:是否可以使用缓存。
  • ifModifiedSince:判断服务器上的文档是否有变化。
  • connected:连接打开为true,关闭为false。

setConnectTimeout()/getConnectTimeout()控制socket等待建立连接的时间,setReadTimeout()/getReadTimeout()控制输入流等待数据到达的时间。

5、配置客户端请求HTTP首部

Web服务器可以根据首部信息向不同的客户端提供不同的页面,获取和设置cookie,通过口令认证用户等。每个URLConnection会在首部默认设置一些不同的名-值对,打开连接前,可以使用setRequestProperty()方法为HTTP首部增加首部字段。如:

        urlConnection.setRequestProperty("Token", "token123456");

6、向服务器写入数据

有时你需要向URLConnection写入数据,例如使用POST向Web服务器提交表单,或者使用PUT上传文件。getOutputStream()方法返回一个OutputStream,可以用来写入数据传送给服务器。

    try {
            URL url = new URL("http://money.innovatelife.net/cost/insertCost");
            //打开连接,准备POST
            URLConnection urlConnection = url.openConnection();
            urlConnection.setDoOutput(true);//设置允许输出,请求方法由GET变为POST
            
            OutputStream outputStream=urlConnection.getOutputStream();
            OutputStream buffered=new BufferedOutputStream(outputStream);
            OutputStreamWriter out=new OutputStreamWriter(buffered,"UTF-8");
            out.write("moneyOut=100&remark=吃饭");
            out.flush();
            out.close();
            
            try(InputStream in=urlConnection.getInputStream()){
                Reader reader=new InputStreamReader(in);
                int c;
                while((c=reader.read())!=-1){
                    System.out.print((char)c);
                }
                System.out.println();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

7、URLConnection的安全考虑

建立网络连接、读/写文件等存在一些常见的安全限制,URLConnection对象会受到这些安全限制的约束。在尝试连接一个URL前,你可能想知道是否允许连接,为此URLConnection类包含了一个getPermission()方法,指出连接这个URL所需的权限。如果不需要任务权限,它会返回null。

8、猜测MIME媒体类型

我们不仅要处理在MIME出现之前已有的老协议,如FTO,而且很多要使用MIME的HTTTP服务器根本不提供MIME首部,或者会提供不正确的首部,URLConnection类提供了两个静态方法,可以帮助程序确定某些数据的MIME类型。guessContentTypeFromName()尝试根据对象URL的文件扩展名部分猜测对象的内容类型,guessContentTypeFromStream()尝试查看流中前几字节来猜测内容类型。

9、HttpURLConnection

java.net.HttpURLConnection类是URLConnection的抽象子类,它提供了另外一些方法,在处理hhtp URL时尤其有帮助。具体地,它包含的方法可以获得和设置请求方法、确定是否重定向、获得响应码和消息,以及确定是否使用了代理服务器。具体的API不再细述。

转载于:https://my.oschina.net/zhaoyi1/blog/886909

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值