HttpURLConnection链接详解

2023-05-16

HttpURLConnection链接详解

一、简介

简单来说,HttpURLConnection 是 Java 提供的发起 HTTP 请求的基础类库,提供了 HTTP 请求的基本功能,不过封装的比较少,在使用时很多内容都需要自己设置,也需要自己处理请求流和响应流。

二、获取连接

获取 HttpURLConnection 对象的方法如下所示:

    // 定义 URL对象
	final URL url = new URL("http//ip:port/xxx");
    // 获取 URL 链接
    URLConnection urlConnection =  url.openConnection();
    // 因为 URL 是根据 url 中的协议(此处http)生成的 URLConnection 类的子类
    // HttpURLConnection, 故此处转换为 HttpURLConnection子类,方便使用子类
    // 中的更多的API
    HttpURLConnection connection = (HttpURLConnection)urlConnection;

三、设置参数

超时时间

在 Http 请求时防止对方长时间无法连接等问题,一般会设置超时时间,可以通过如下方式设置

 	// 设置连接超时时间, 值必须大于0,设置为0表示不超时 单位为“毫秒”
    connection.setConnectTimeout(30000);   
	// 设置读超时时间, 值必须大于0,设置为0表示不超时 单位毫秒
    connection.setReadTimeout(60000);

设置请求方法

在 Http 请求中包括 GET、POST、PUT等方法,可以通过如下方法设置 HttpURLConnection的请求方法

// 设置为 GET 请求, 
connection.setRequestMethod("GET");

注意:此处 方法必须设置为 大写,否则会报如下错误

java.net.ProtocolException: Invalid HTTP method: get

可以通过定义枚举类型设置,如下所示:

// 定义请求方法枚举类  
public enum HttpMethod {
    GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
  }

// 在使用枚举设置请求方法
connection.setRequestMethod(HttpMethod.POST.name());

请求头信息

在请求时,经常会遇到设置自定义请求头,或者更改 Conent-Type 的值,可以通过如下方设置:

// 设置请求类型为 application/json
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
// 设置可接受的数据类型
connection.setRequestProperty("Accept", "*/*");
// 设置保持长链接
connection.setRequestProperty("Connection", "Keep-Alive");
// 设置自定义头 Token
connection.setRequestProperty("Token", "123456");

其他参数

// 设置不使用缓存, 默认为 true 使用缓存
connection.setUseCaches(false);
// 设置单次请求是否支持重定向,默认为 setFollowRedirects 方法设置的值
connection.setInstanceFollowRedirects(false);

// 设置是否进行重定向,注意此处为 静态方法,表示所有的请求都不支持重定向,默认为true
HttpURLConnection.setFollowRedirects(false);

注意:

所有的参数,必须在建立连接之前设置,否则会报如下错误

java.lang.IllegalStateException: Already connected

四、建立连接

显式连接

在设置完所有参数后,可以通过调用 connect 方法,进行显式建立连接。如下所示:

    // 调用打开连接, 调用此方法,只是建立一个连接,并不会发送数据。 
    connection.connect();

隐式连接

除了上面的调用 connect 显式建立连接外,在调用如下方法时,会隐式的调用此方法,建立连接。

    // 获取输出流
    connection.getOutputStream();
    // 获取输入流
    connection.getInputStream();

由于,在网络请求时,一般都会获取请求结果,故在实际应用中,一般不调用 connect() 方法进行显式打开连接。

五、发送数据

POST请求

众所周知,HTTP 中的 POST 请求的数据是包含在请求体中的。在 HttpURLConnection 中 POST 请求发送数据时,需要获取 连接的输出流对象,然后往输出流中写数据即可,如下所示:

    // 要发送的数据
    String connect = "我是一个POST请求数据";

    // 因为这个是post请求,参数要放在
    // http正文内,因此需要设为true, 默认情况下是false;
    connection.setDoOutput(true);

    // 从连接中获取 输出流对象
    OutputStream os = connection.getOutputStream();
    // 往输出流中写数据
    os.write(connect.getBytes(StandardCharsets.UTF_8));
    // 冲刷 并 关闭输出流
    os.flush();
    os.close();

注意:

1、 需要写数据时,必须调用 connection.setDoOutput(true); 方法,并且参数为 true, 且需要在调用 getOutputStream() 方法之前调用。

2、此时写的数据,只是写到了缓冲区中,并不会真正的把数据发送给资源方。

GET请求

Http 中的 GET 请求的参数是拼接在 URL 后进行发送的,所以 发送 GET 请求时,在创建 连接时把参数拼接在后面即可。

但是,有一点需要注意,如果在 GET 请求中 也调用了 getOutputStream() 方法,那么,自动就会把请求改为 POST 请求。如下源码所示:

// HttpURLConnection 中的 方法,
private synchronized OutputStream getOutputStream0() throws IOException {
        try {
            if (!this.doOutput) {
               
            } else {
                // 如果设置的 方法为 GET 则改为 POST
                if (this.method.equals("GET")) {
                    this.method = "POST";
                }
            }
        }catch(Exception e){
            
        }
}

六、响应数据

请求结果

在 HTTP 请求中一般是需要知道请求状态,在 HttpURLConnection 中可以通过如下方式获取请求状态

// 获取请求状态,此状态即为 HTTP 请求的状态 200:成功,404:找不到资源 等
int responseCode = connection.getResponseCode();

// 获取请求描述信息
String msg = connection.getResponseMessage();

获取头信息

获取响应头有如下几种方式:

// 1、获取所有的响应头信息
 Map<String, List<String>> headerFields = connection.getHeaderFields();

// 2、根据头信息名称获取响应头信息
String connectionHeader =  connection.getHeaderField("Connection");

// 3、根据头信息索引获取响应头信息, 此下标 必须大于 0。
String secHeader = connection.getHeaderField(2);

读取数据

读取响应数据也是比较简单的,可以首先通过 HttpURLConnection 中的 getInputStream() 方法 获取输入流,然后,通过输入流获取数据即可,如下所示:

	// 获取输入流
	InputStream inputStream = connection.getInputStream();
	// 定义一个临时字节输出流
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        // 开始读取数据
        byte[] buffer = new byte[256];
        int len = 0;
        while ((len = inputStream.read(buffer)) > 0){
            baos.write(buffer,0, len);
        }
        return new String(baos.toByteArray(), StandardCharsets.UTF_8);
    } finally {
        // 关闭输入、输出流
        inputStream.close();
        baos.close();
    }

七、上传下载

上传

在普通 Web 页面中上传文件是很简单的,只需要把 from 标签中加上 enctype="multipart/form-data" 即可,剩下的都交给浏览器去完成发送数据的收集并发送 Http 请求即可。

但是,在 HttpURLConnection 中脱离了浏览器,就需要我们自己去完成数据收集并发送请求了。那么我们首先看下浏览器是怎么收集上传数据,并发送请求的。

先看下浏览器发送上传时间的请求正文格式:

// 请求头中的 Content-Type 属性 其中定义了属性分割线  
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryfdRf0g4cPSTVeLkJ

// 请求数据正文信息
------WebKitFormBoundaryfdRf0g4cPSTVeLkJ
Content-Disposition: form-data; name="images"; filename="20150703212056_Yxi4L.jpeg"
Content-Type: image/jpeg


------WebKitFormBoundaryfdRf0g4cPSTVeLkJ
Content-Disposition: form-data; name="checkRecord"

{"describe":"","rectify":"立即整改"}
------WebKitFormBoundaryfdRf0g4cPSTVeLkJ--

分析上面的的数据我们能够发下如下规则:

  1. 数据正文中的第一行 ------WebKitFormBoundaryfdRf0g4cPSTVeLkJ 作为分隔符,然后是 \r\n 回车换行符。
  2. 第二行 Content-Disposition: form-data; name="images"; filename="*****", 代表 form 表单数据域,其中 name 表示 接口属性值,filename 为文件名称。
  3. 第三行 Content-Type: image/jpeg 表示上传文件的类型。
  4. 第四行是一个 回车换行符。
  5. 第五行 是 数据内容,由于此处为 图片故没有显示出来。
  6. 后面的也是遵从上述规律。
  7. 最后一行表示结束行,注意后面多两个--

根据以上规律,我们 在 使用 HttpURLConnection 进行上传时,就可以按照此规律拼接发送的数据流。实例如下所示:

public void upload(File file) throws Exception {
    final URL url = new URL("http://localhost:10010/user/upload");

    // 获取 URL 链接
    URLConnection urlConnection =  url.openConnection();
    // 因为 URL 是根据 url 中的协议(此处http)生成的 URLConnection 类的子类
    // HttpURLConnection, 故此处转换为 HttpURLConnection子类,方便使用子类
    // 中的更多的API
    HttpURLConnection connection = (HttpURLConnection)urlConnection;

    // 自定义分割线,并设置请求头信息
    String boundary = "------------" + System.currentTimeMillis();

    connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
    // 设置请求为 POST 请求
    connection.setRequestMethod(METHOD.POST.name());
    // 打开输出流
    connection.setDoOutput(true);
    // 获取上传文件的类型
    MagicMatch magicMatch = Magic.getMagicMatch(file, false, true);
    String mimeType = magicMatch.getMimeType();

    // 获取输出流
    OutputStream outputStream = connection.getOutputStream();

    //拼接请求数据
    StringBuilder builder = new StringBuilder();
    // 第一行分割行
    builder.append("\r\n").append("--" + BOUNDARY).append( "\r\n");
    // 第二行form表单数据
    builder.append("Content-Disposition: form-data; name=\"file\"; filename=\"").append(file.getName() ).append("\"\r\n");
    // 第三行 上传数据类型
    builder.append( "Content-Type:").append(mimeType).append("\r\n");
    // 第四行一个空行
    builder.append("\r\n");
    outputStream.write(builder.toString().getBytes(StandardCharsets.UTF_8));
    // 开始写文件数据
    InputStream fileInput = new FileInputStream(file);
    byte[] buffer = new byte[512];
    int len = 0;
    while ((len = fileInput.read(buffer)) > 0){
        outputStream.write(buffer, 0, len);
    }


    // 开始写基本数据
    StringBuilder textBuffer = new StringBuilder();
    // 分隔符行
    textBuffer.append("\r\n").append("--" + BOUNDARY).append("\r\n");
    // form表单数据
    textBuffer.append("Content-Disposition: form-data; name=\"name\"\r\n");
    // 一个空行
    textBuffer.append("\r\n");
    // 数据值
    textBuffer.append("张三");
    outputStream.write(textBuffer.toString().getBytes(StandardCharsets.UTF_8));

    // 写入结束行
    outputStream.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes(StandardCharsets.UTF_8));
    outputStream.flush();
    outputStream.close();
    fileInput.close();
    int responseCode = connection.getResponseCode();
    printHeaders(connection.getHeaderFields());
    if(responseCode != 200){
        LOGGER.error("请求失败, code: {}, message: {}", responseCode, connection.getResponseMessage());
    }else {
        InputStream inputStream = connection.getInputStream();
        String reader = reader(inputStream);
        LOGGER.info("服务端返回数据为: \n {}", reader);
    }
}

注意:

基本数据比 file 缺少 Content-Type: image/jpeg 行

下载

文件的下载就比较简单了,获取输入流,然后读取输入流,并把读到的数据保存到本地即可,一下是下载网络上的图片为例。

/**
     * 下载
     * @param url 下载文件路径
     * @param distDir 保存的文件路径
     */
   public void download(String url, String distDir) throws Exception {
       // 获取连接
       HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();

       // 设置请求方法
       connection.setRequestMethod("GET");
       connection.setRequestProperty("Charset", "UTF-8");

       // 获取文件名
       String fileUrl = connection.getURL().getFile();
       String fileName = fileUrl.substring(fileUrl.lastIndexOf(File.separatorChar) + 1);

       LOGGER.info("文件名:{} -- {}", fileName,  File.separator);
       String filePath = distDir + File.separatorChar + fileName;
       File file = new File(filePath);
       if(!file.getParentFile().exists()){
           file.getParentFile().mkdirs();
       }
       // 获取输入流,并写入文件
       try (InputStream inputStream = connection.getInputStream();
            OutputStream os = new FileOutputStream(file)) {
           byte[] buffer = new byte[256];
           int len = 0;
           while ((len = inputStream.read(buffer)) > 0) {
               os.write(buffer, 0, len);
           }
           os.flush();
       }

   }

九、总结

  1. 通过 URL 类的 openConnection() 方法获取请求连接
  2. 如果需要写入数据需要调用 setDoOutput(true) 打开输出流
  3. 在调用connection()getOutputStream()getInputStream() 方法之前设置好请求参数。
  4. 如果调用 getOutputStream() 方法,则会把把请求方法改为 POST
  5. 建立连接和调用 getOutputStream() 方法写入数据并关闭连接后,也不会发送数据,只有调用 getInputStream()才会真正的发送数据。
  6. 在使用 HttpURLConnection 上传数据时,需要仿照浏览器上传,手动拼接数据格式

关注【猿博园】回复“http” 可获取源码
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HttpURLConnection链接详解 的相关文章

随机推荐

  • 用python操作浏览器的三种方式

    第一种 xff1a selenium导入浏览器驱动 xff0c 用get方法打开浏览器 xff0c 例如 xff1a import time from selenium import webdriver def mac driver 61
  • Linux-虚拟网络设备-veth pair

    基本概念 Virtual Ethernet CableBidirectional FIFOOften used to cross namespaces Linux container 中用到一个叫做veth的东西 xff0c 这是一种新的设
  • openstack-neutron-OVS agent(持续更新)

    概述 ML2Plugin的主要工作是管理虚拟网络资源 xff0c 保证数据正确无误 xff0c 具体物理设备的设置则由Agent完成 L2Agent通常运行在Hypervisor xff0c 与neutron server通过RPC通信 x
  • VUE中使用EventSource接收服务器推送事件

    Vue项目中 xff0c EventSource触发的事件中this指向变了 使用const that 61 this xff0c 然后在EventSource触发的事件中使用that if typeof EventSource 61 61
  • VNC

    一 安装tigervnc server 二 配置登录帐号 三 生成xstartup与log日志 注意 xff1a 如果没有使用vncserver来 设置密码 xff0c 则service vncserver restart 是不会成功的 这
  • DirectUI框架GUIFW

    前言 guifw是一款基于GDI 43 的DirectUI皮肤引擎 xff0c 借鉴了DuiLib和Qt的思想 效果预览 xff1a http download csdn net detail sllins 7707771 代码已开源 xf
  • keil 提示internal command error和no sw device

    1 使用keil烧录软件的时候 xff0c jlink stlink无法识别到芯片 需要排查的问题 1 xff09 换条线 2 xff09 是不是有程序禁用了Seral Wire xff1a 使用cubeide cubeMX xff0c 容
  • 多线程编程模式之Single Threaded Execution 模式

    一 Single Threaded Execution 模式介绍 简单的来说 xff0c Single threaded execution 模式描述了在一种多线程环境下各个线程对于公用资源的使用方式 任一时刻 xff0c 只有一个线程可以
  • NVIDIA Jetson TX2 查看系统相关+运行demo

    1 查看Jetson TX2 L4T版本 xff1a head n 1 etc nv tegra release 2 查看系统版本 xff1a cat etc lsb release 3 查看系统内核 xff1a uname a 4 查看内
  • Docker镜像迁移至新的服务器(全部数据)

    1 找到你想移动的 Docker 容器的 ID 2 提交你的变更 xff0c 并且把容器保存成镜像 xff0c 命名为 newimage docker commit span class token number 3 span a09b25
  • 配置VNC环境在windows主机访问阿里云linux服务器

    配置VNC环境在windows主机访问阿里云linux服务器 虽然作为服务器使用更多的是使用字符终端连接服务器 xff0c 进行操作 xff0c 因为图形界面很消耗性能和资源 xff0c 但有的时候使用图形界面进行操作更为便捷 xff0c
  • pythondataframe输出小结

    在使用dataframe时遇到datafram在列太多的情况下总是自动换行显示的情况 xff0c 导致数据阅读困难 xff0c 效果如下 xff1a coding utf 8 import numpy as np import pandas
  • 聊聊 Redis 为什么构建自己的简单动态字符串 SDS

    我们知道 xff0c Redis 支持字符串 哈希 列表 集合和有序集合五种基本类型 那么我们如何把图片 音频 视频或者压缩文件等二进制数据保存到 Redis 中呢 xff1f 之前在使用 Memcached 缓存这类数据时是把它们转换成
  • 聊聊 Redis 高可用之持久化AOF和RDB分析

    Redis 持久化概述 Redis 是内存数据库 xff0c 数据都是存储在内存中 xff0c 为了避免进程退出导致数据的永久丢失 xff0c 需要定期将 Redis 中的数据以某种形式把内存中的数据保存到磁盘中 xff1b 当 Redis
  • mysqldump: Got error: 1044: Access denied for user XXXX when doing LOCK TABLES

    一 报错信息 在使用mysqldump 执行远程备份数据库的时候报如下错误 xff1a mysqldump Got error span class token number 1044 span Access denied span cla
  • jmap -heap [pid]运行报:Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException(不允许的操作)

    一 运行环境 操作系统 xff1a Ubuntu 5 4 0 6 Java版本 xff1a JDK8 二 执行命令 jmap heap span class token punctuation span pid号 span class to
  • chkconfig: command not found

    问题描述 在 ubuntu1 16 04 10 执行 chkconfig 命令报 chkconfig command not found 说明此服务上没有安装 chkconfig 执行如下命令进行安装 span class token fu
  • Docker 基础篇 之 安装

    一 Docker安装 查看 CentOS 内核版本 Docker 要求 CentOS 系统的内核版本高于3 10 执行如下命令查询 内核版本 span class token function uname span r span class
  • Java 基础 之 Valid 验证

    一 64 Valid 简介 Bean Validation 内置的校验器 校验器说明 64 Null被注解的元素必须为 null 64 NotNull被注解的元素必须不为 null 64 AssertTrue被注解的元素必须为 true 6
  • HttpURLConnection链接详解

    HttpURLConnection链接详解 一 简介 简单来说 xff0c HttpURLConnection 是 Java 提供的发起 HTTP 请求的基础类库 xff0c 提供了 HTTP 请求的基本功能 xff0c 不过封装的比较少