HttpURLConnection详解--类似postman请求--上传下载

一、简介

简单来说,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 上传数据时,需要仿照浏览器上传,手动拼接数据格式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值