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--
分析上面的的数据我们能够发下如下规则:
- 数据正文中的第一行 ------WebKitFormBoundaryfdRf0g4cPSTVeLkJ 作为分隔符,然后是
\r\n
回车换行符。 - 第二行
Content-Disposition: form-data; name="images"; filename="*****"
, 代表 form 表单数据域,其中 name 表示 接口属性值,filename 为文件名称。 - 第三行
Content-Type: image/jpeg
表示上传文件的类型。 - 第四行是一个 回车换行符。
- 第五行 是 数据内容,由于此处为 图片故没有显示出来。
- 后面的也是遵从上述规律。
- 最后一行表示结束行,注意后面多两个
--
。
根据以上规律,我们 在 使用 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 行
下载
文件的下载就比较简单了,获取输入流,然后读取输入流,并把读到的数据保存到本地即可,一下是下载网络上的图片为例。文章来源:https://www.toymoban.com/news/detail-416582.html
/**
* 下载
* @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();
}
}
九、总结
- 通过
URL
类的openConnection()
方法获取请求连接 - 如果需要写入数据需要调用
setDoOutput(true)
打开输出流 - 在调用
connection()
、getOutputStream()
、getInputStream()
方法之前设置好请求参数。 - 如果调用
getOutputStream()
方法,则会把把请求方法改为POST
- 建立连接和调用
getOutputStream()
方法写入数据并关闭连接后,也不会发送数据,只有调用getInputStream()
才会真正的发送数据。 - 在使用 HttpURLConnection 上传数据时,需要仿照浏览器上传,手动拼接数据格式
关注【猿博园】回复“http” 可获取源码
文章来源地址https://www.toymoban.com/news/detail-416582.html
到了这里,关于HttpURLConnection链接详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!