网络编程总结(四):URL、URLConnection和HttpURLConnection

URL

统一资源定位符(URL),有时候也被俗称为“网址”,它们的存在就如同在网络上的门牌,是网络上标准资源的地址,是网络中最基础的部分。

URL 标准格式如下:

协议类型://服务器地址:端口/路径/文件名

Java 提供了一系列 API 来解析 URL 的,捡常用的说一下:

public class NetTest {
    public static void main(String[] args) {
        try {
            URL url = new URL("https://www.li-xyz.com/content/images/2017/02/sp170223_205027.png");
            System.out.println("此 URL 的文件名:"+url.getFile());
            System.out.println("此 URL 的主机名:"+url.getHost());
            System.out.println("此 URL 的路径:"+url.getPath());
            System.out.println("此 URL 的协议:"+url.getProtocol());
            System.out.println("此 URL 的端口:"+url.getPort());
            System.out.println("此 URL 的查询部分:"+url.getQuery());
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

执行结果

此 URL 的文件名:/content/images/2017/02/sp170223_205027.png
此 URL 的主机名:www.li-xyz.com
此 URL 的路径:/content/images/2017/02/sp170223_205027.png
此 URL 的协议:https
此 URL 的端口:-1
此 URL 的查询部分:null

因为我们没有指定端口,所以返回值为 -1。

为啥查询部分是 null 呢?

我们把 URL 修改一下:

URL url = new URL("https://www.li-xyz.com/content/images?username=123&password=456");

这个时候 URL 的查询结果就是:

username=123&password=456

URL 还有两个重要的方法:openConnection() 和 openStream()。

openConnection()

该方法返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。获取到了连接之后,我们就可以读取或者写入此 URL 所代表的资源,在下面会讲到。

HttpURLConnection connection = (HttpURLConnection) url.openConnection();

openStream()

此打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream,然后就可以读取 URL 所指向的文件的内容了
一个小例子:

public class NetTest {

    public static void main(String[] args) {

        try {
            URL url = new URL("https://www.li-xyz.com/content/images/2017/02/sp170223_205027.png");
            InputStream is = url.openStream();
            byte[] buf = new byte[1024];
            int len = 0;
            FileOutputStream fos = new FileOutputStream(new File("D:\\newPic.png"));
            while ((len = is.read(buf)) != -1) {
                fos.write(buf, 0, len);
                System.out.println("111");
                fos.flush();
            }
            fos.close();
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

然后就将这个图片下载到本地了。

URLConnection

URLConnection 是一个抽象类,代表着我们的客户端和 URL 之间的通信链接,此类的实例可用于读取和写入此 URL 引用的资源。通常,创建一个到 URL 的连接需要几个步骤:

  1. 通过在 URL 上调用 openConnection() 方法创建连接对象。
  2. 处理设置参数和一般请求属性
  3. 使用 connect() 方法建立到远程对象的实际连接
  4. 远程对象变为可用,开始处理远程对象(读、些、删等操作)

Java 提供了一下方法来帮我们修改连接参数

  • setAllowUserInteraction
    对 allowUserInteraction 字段进行设置,如果设置为 true,则允许用户交互的上下文中对此 URL 进行检查,如果设置为 false,则不允许有任何用户交互,默认 allowUserInteraction 值为 false,也可以调用 setDefaultAllowUserInteraction 方法修改 allowUserInteraction 的默认值。
  • setDoInput<tr>设置 doInput 字段的值,URL 连接可用户输入或者输出,将该值设置为true,表示应用程序要从 URL 读取数据,doInput 默认为true。
  • setDoOutput<tr>设置 doOutput 字段的值,URL 连接可用户输入或输出连接,将 doOutput 设置为true,表示应用程序要向 URL 中写入数据,doOutput 默认为false。
  • setIfModifiedSince
    设置 ifModifiedSince 字段的值,该字段默认为0,该值表示距离格林威治标准时间 1970 年 1 月 1 日的毫秒数。只有在该时间之后又进行了修改时,才获取该对象。
  • setUserCaches
    设置 useCaches 字段的值,该字段如果为 true,则只要有条件就允许协议使用缓存。如果为 false,则该协议始终必须获得此对象的新副本。

使用 setRequestProperty 方法来修来请求属性。

请求属性就不用多说了,在《网络编程总结(二):HTTP 协议》一文中列取了常用的请求属性,当然我们也可以自定义我们自己的请求属性。

在获取到远程连接之后,可以使用以下方法来获取访问头字段和内容:

  • getContent
    获取此 URL 连接的内容。
  • getHeaderField
    该方法可以传入两种参数,当传入 int 值时,则返回第 n 个头字段的值,如果传入 String 值,则返回指定的头字段的值。
  • getInputStream
    返回从此打开的连接读取的输入流。 在读取返回的输入流时,如果在数据可供读取之前达到读入超时时间,则会抛出 SocketTimeoutException。
  • getOutputStream
    返回写入到此连接的输出流。
  • getContentEncoding
    返回 content-encoding 头字段的值。
  • getContentLength
    返回 content-length 头字段的值。
  • getContentType
    返回 content-type 头字段的值。
  • getDate
    返回 date 头字段的值。
  • getExpiration
    返回 expires 头字段的值。
  • getLastModifed
    返回 last-modified 头字段的值。结果为距离格林威治标准时间 1970 年 1 月 1 日的毫秒数。

我们经常使用的是 URLConnection 的子类 HttpURLConnection。

HttpURLConnection

HttpURLConnection 是支持 HTTP 特定功能的 URLConnection,每个 HttpURLConnection 实例都可用于生成单个请求,但是其他实例可以透明地共享连接到 HTTP 服务器的基础网络。请求后在 HttpURLConnection 的 InputStream 或 OutputStream 上调用 close() 方法可以释放与此实例关联的网络资源,但对共享的持久连接没有任何影响。如果在调用 disconnect() 时持久连接空闲,则可能关闭基础套接字。

HttpURLConnection 有几个重要的方法:

  • setRequestMethod
    设置 URL 请求的方法,具体有哪些方法,参考 《网络编程总结(二):HTTP 协议》
  • getResponseCode
    从 HTTP 响应消息获取状态码
  • getRequestMethod
    获取请求方法。
  • getResponseMessage
    获取与来自服务器的响应代码一起返回的 HTTP 响应消息(如果有)。

一个简单的小例子:

服务器端就是简单的从请求中获取到输入流,然后输出到本地。
UploadServlet

public class WelcomeServlet extends HttpServlet {
    private String greeting;

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("doPost方法");

        ServletInputStream is = req.getInputStream();
        byte[] buf = new byte[1024];
        int len = 0;
        FileOutputStream fos = new FileOutputStream(
                new File("D:\\HAHAHAHA.PNG"));

        while ((len = is.read(buf)) != -1) {
            fos.write(buf, 0, len);
            fos.flush();
        }
        is.close();
        fos.close();
        System.out.println("OK");
    }

    @Override
    protected void service(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        if (request.getMethod().equals("GET")) {
            doGet(request, response);
        } else if (request.getMethod().equals("POST")) {
            doPost(request, response);
        }
    }
}

客户端获取到连接,然后获取输出流,将文件输出

public class NetTest {

    public static void main(String[] args) {
        try {
            URL url = new URL("http://127.0.0.1:8080/ServletTest/upload");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setDoOutput(true);

            OutputStream os = conn.getOutputStream();
            FileInputStream fis = new FileInputStream(new File("C:\\Users\\LGB\\Desktop\\Pics\\sp170223_185642.png"));
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = fis.read(buf)) != -1) {
                os.write(buf, 0, len);
                os.flush();
            }
            System.out.println("OK");
            os.close();
            fis.close();
            InputStream inputStream = conn.getInputStream();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}