Apache httpclient & okhttp(1)
Apache httpclient & okhttp(2)
okhttp github
okhttp官方使用文档
okhttp官方示例代码
OkHttp使用介绍
OkHttp使用进阶 译自OkHttp Github官方教程
SpringBoot 整合okHttp okhttp3用法
Java中常用的HTTP客户端库:OkHttp和HttpClient(包含请求示例代码)
深入浅出 OkHttp 源码解析及应用实践
HTTP是现代应用程序的网络方式。这是我们交换数据和媒体的方式。高效地使用HTTP可以让您的东西加载更快并节省带宽。
OkHttp使用起来很方便。它的请求/响应API设计具有流式构建和不可变性。它支持同步阻塞调用
和带有回调的异步调用
。
OkHttp是一个高效的默认HTTP客户端
OkHttp遵循现代HTTP规范,例如
注意:okhttp的3.9.0版本用的是java,okhttp4.12.0用的是kotlin。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.zzhuagroupId>
<artifactId>demo-okhttpartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<okhttp.version>4.12.0okhttp.version>
properties>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3groupId>
<artifactId>okhttpartifactId>
<version>${okhttp.version}version>
dependency>
dependencies>
project>
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
import okhttp3.*;
public class Test02 {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
"{\"username\":\"zzhua\"}");
Request request = new Request.Builder()
.url("http://localhost:8080/ok01")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
这些示例代码来自:https://square.github.io/okhttp/recipes,
官方也有对应的代码:https://github.com/square/okhttp/blob/okhttp_3.14.x/samples
下载文件,打印headers,并将其响应正文打印为字符串。
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class TestSynchronous {
private final static OkHttpClient client = new OkHttpClient();
public static void main(String[] args) {
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
// 获取响应头
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
// 获取响应体
System.out.println(response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
在工作线程中下载文件,并在响应可读取时触发回调。该回调会在响应头准备就绪后触发,但读取响应体仍可能阻塞线程。当前OkHttp未提供异步API以分块接收响应体内容。
@Slf4j
public class Test02Async {
public static void main(String[] args) {
log.info("main start");
OkHttpClient okHttpClient = new OkHttpClient();
Request requeset = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
Call call = okHttpClient.newCall(requeset);
log.info("call.enqueue");
// 执行回调的线程不是main线程
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
log.info("=========请求失败=========: {}", e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
log.info("=========获得响应=========");
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(responseBody.string());
}
}
});
log.info("main end");
}
}
public class TestHeader {
public static void main(String[] args) {
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues")
// 单个header值
.header("User-Agent", "OkHttp Headers.java")
// 多个header值
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
System.out.println("Server: " + response.header("Server"));
System.out.println("Date: " + response.header("Date"));
// 多个响应头使用headers()获取
System.out.println("Vary: " + response.headers("Vary"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
使用HTTP POST向服务发送请求正文。此示例将markdown文档发布到将markdown渲染为html。由于整个请求正文同时在内存中,因此避免使用此API发布大型(大于1 MiB)文档。
public class TestRequestBody {
public static void main(String[] args) {
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String postBody = ""
+ "Releases\n"
+ "--------\n"
+ "\n"
+ " * _1.0_ May 6, 2013\n"
+ " * _1.1_ June 15, 2013\n"
+ " * _1.2_ August 11, 2013\n";
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(mediaType, postBody))
.build();
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
我们在这里将请求体以流的形式进行POST提交。该请求体的内容在写入过程中动态生成。此示例直接将数据流式写入Okio的缓冲池(Buffered Sink)。您的程序可能更倾向于使用OutputStream,您可以通过BufferedSink.outputStream()方法获取它。
public class Test05Stream {
public static void main(String[] args) {
MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new RequestBody() {
@Override public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("Numbers\n");
sink.writeUtf8("-------\n");
for (int i = 2; i <= 997; i++) {
sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
}
}
private String factor(int n) {
for (int i = 2; i < n; i++) {
int x = n / i;
if (x * i == n) return factor(x) + " × " + i;
}
return Integer.toString(n);
}
};
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
通过观察客户端和服务端的日志,可以看到客户端发的同时,服务端也在收。
@Slf4j
public class Test05StreamClient {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient.Builder()
.writeTimeout(30, TimeUnit.SECONDS) // 延长写入超时
.build();
// 创建流式 RequestBody
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse("application/octet-stream");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
try (java.io.OutputStream os = sink.outputStream()) {
for (int i = 0; i < 50; i++) {
// 模拟生成数据块
String chunk = "Chunk-" + i + "\n";
log.info("发送数据块: {}", chunk);
os.write(chunk.getBytes());
os.flush(); // 立即刷新缓冲区
Thread.sleep(100); // 模拟延迟
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
Request request = new Request.Builder()
.url("http://localhost:8080/stream-upload-raw")
.post(requestBody)
.header("Content-Type", "application/octet-stream") // 强制覆盖
.build();
// 异步执行
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
log.info("[请求失败]");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
log.info("[请求成功]");
System.out.println("上传成功: " + response.code());
System.out.println("响应内容: " + response.body().string());
response.close();
}
});
}
}
@Slf4j
@RestController
public class StreamController {
// 通过 HttpServletRequest 获取原始流
@PostMapping("/stream-upload-raw")
public String handleRawStream(HttpServletRequest request) {
try (InputStream rawStream = request.getInputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = rawStream.read(buffer)) != -1) { // 按字节读取
String chunk = new String(buffer, 0, bytesRead);
log.info("[字节流] {}, {}", chunk.trim(), bytesRead);
}
return "Raw stream processed";
} catch (IOException e) {
return "Error: " + e.getMessage();
}
}
}
将文件作为请求体。
public class Test06File {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://localhost:8080/okFile")
.post(RequestBody.create(null, new File("C:\\Users\\zzhua195\\Desktop\\test.png")))
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
后端代码
@RequestMapping("okFile")
public Object okFile(HttpServletRequest request) throws Exception {
ServletInputStream inputStream = request.getInputStream();
org.springframework.util.FileCopyUtils.copy(request.getInputStream(), new FileOutputStream("D:\\Projects\\practice\\demo-boot\\src\\main\\resources\\test.png"));
return "ok";
}
使用FormBody.Builder构建一个像超文本标记语言