Java HttpClient
Standard HTTP client API provided from Java 11+. Modern and fluent API design with HTTP/2 support and both asynchronous/synchronous capabilities. Built into JDK with no external dependencies, provides WebSocket support and CompletableFuture-based async processing.
GitHub Overview
openjdk/jdk
JDK main-line development https://openjdk.org/projects/jdk
Topics
Star History
Library
Java HttpClient
Overview
Java HttpClient is the standard HTTP client library in the "java.net.http" package officially introduced in Java 11. First appearing as an incubation feature in Java 9, it supports both HTTP/1.1 and HTTP/2, corresponding to synchronous and asynchronous programming models as a modern HTTP communication solution. As the official successor to the traditional HttpURLConnection, it features reactive stream support, builder pattern adoption, and complete SSL/TLS support, enabling production-quality HTTP communication without external dependencies.
Details
Java HttpClient 2025 edition provides maturity and stability as a modern HTTP implementation within the Java standard library. Complete support for the HTTP/2 protocol enables efficient multiplexed communication, comprehensively providing CompletableFuture-based asynchronous APIs, reactive stream body processing, and automatic redirect, authentication, and proxy support. Being included in the JDK standard eliminates the need for external dependencies, making adoption easy in enterprise environments. It is positioned as the recommended HTTP client in major frameworks such as Spring Boot and Quarkus.
Key Features
- JDK Standard Inclusion: Available without external dependencies from Java 11 onwards
- Complete HTTP/2 Support: Multiplexing, server push, and header compression support
- Synchronous & Asynchronous Support: Flexible programming model through CompletableFuture
- Reactive Streams: Efficient request and response body processing
- Builder Pattern: Intuitive and type-safe API design
- Comprehensive Security: Complete support for SSL/TLS, authentication, and certificate verification
Pros and Cons
Pros
- JDK standard library with no external dependencies and long-term support guarantee
- Performance improvement in high-performance parallel communication and resource efficiency through HTTP/2 support
- Natural asynchronous programming experience through CompletableFuture
- Type-safe and maintainable builder pattern API
- Easy adoption in enterprise environments, passes security audits
- Recommended client in major frameworks such as Spring Boot
Cons
- Limited to Java 11 and later, unavailable in legacy environments
- Limited high-level features compared to third-party libraries
- Complex detailed settings such as HTTPS proxy configuration
- Somewhat low-level streaming processing API
- Basic debugging and logging output features
- Requires custom implementation for retry, circuit breaker, etc.
Reference Pages
Code Examples
Basic Setup and Dependencies
// No additional dependencies required for Java 11 and later
// module-info.java (when using module system)
module myapp {
requires java.net.http;
}
# Java environment verification
java -version # Confirm Java 11 or later
# Project creation example (Maven case)
mvn archetype:generate -DgroupId=com.example.httpclient \
-DartifactId=java-httpclient-demo \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DinteractiveMode=false
# Configure JDK 11 or later (pom.xml)
<!-- pom.xml -->
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
Basic HTTP Requests (GET, POST, PUT, DELETE)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
public class BasicHttpClientExample {
public static void main(String[] args) throws Exception {
// HttpClient creation
HttpClient client = HttpClient.newHttpClient();
// Basic GET request
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Status Code: " + response.statusCode());
System.out.println("Headers: " + response.headers().map());
System.out.println("Body: " + response.body());
}
// HttpClient creation with custom settings
public static void customHttpClientExample() throws Exception {
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // Prefer HTTP/2
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(20))
.build();
// GET request with query parameters
String url = "https://api.example.com/users?page=1&limit=10&sort=created_at";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Accept", "application/json")
.header("User-Agent", "MyApp/1.0 (Java HttpClient)")
.timeout(Duration.ofMinutes(1))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("Success: " + response.body());
} else {
System.out.println("Error: " + response.statusCode());
}
}
// POST request (JSON sending)
public static void postJsonExample() throws Exception {
HttpClient client = HttpClient.newHttpClient();
String jsonBody = """
{
"name": "John Doe",
"email": "[email protected]",
"age": 30
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer your-token")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 201) {
System.out.println("User creation completed: " + response.body());
} else {
System.out.println("Error: " + response.statusCode() + " - " + response.body());
}
}
// PUT and DELETE requests
public static void putDeleteExample() throws Exception {
HttpClient client = HttpClient.newHttpClient();
// PUT request (data update)
String updatedJsonBody = """
{
"name": "Jane Doe",
"email": "[email protected]",
"age": 31
}
""";
HttpRequest putRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/123"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer your-token")
.PUT(HttpRequest.BodyPublishers.ofString(updatedJsonBody))
.build();
HttpResponse<String> putResponse = client.send(putRequest,
HttpResponse.BodyHandlers.ofString());
System.out.println("Update status: " + putResponse.statusCode());
// DELETE request
HttpRequest deleteRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/123"))
.header("Authorization", "Bearer your-token")
.DELETE()
.build();
HttpResponse<String> deleteResponse = client.send(deleteRequest,
HttpResponse.BodyHandlers.ofString());
if (deleteResponse.statusCode() == 204) {
System.out.println("User deletion completed");
}
}
}
Asynchronous Requests and CompletableFuture
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.List;
import java.util.stream.Collectors;
public class AsyncHttpClientExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
// Single asynchronous request
asyncSingleRequest(client);
// Multiple parallel asynchronous requests
asyncMultipleRequests(client);
// Asynchronous chained processing
asyncChainedRequests(client);
}
// Single asynchronous request
public static void asyncSingleRequest(HttpClient client) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Accept", "application/json")
.GET()
.build();
CompletableFuture<HttpResponse<String>> futureResponse =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
// Wait for asynchronous processing result
HttpResponse<String> response = futureResponse.get();
System.out.println("Async response: " + response.statusCode());
// Or process with callback
futureResponse.thenAccept(resp -> {
System.out.println("Callback processing: " + resp.body());
}).join();
}
// Multiple parallel asynchronous requests
public static void asyncMultipleRequests(HttpClient client) throws Exception {
List<String> urls = List.of(
"https://api.example.com/users",
"https://api.example.com/posts",
"https://api.example.com/comments",
"https://api.example.com/categories"
);
// Start parallel requests
List<CompletableFuture<HttpResponse<String>>> futures = urls.stream()
.map(url -> HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Accept", "application/json")
.GET()
.build())
.map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
.collect(Collectors.toList());
// Wait for all completions
CompletableFuture<List<HttpResponse<String>>> allFutures =
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
List<HttpResponse<String>> responses = allFutures.get();
for (int i = 0; i < responses.size(); i++) {
HttpResponse<String> response = responses.get(i);
System.out.println("URL " + urls.get(i) + " - Status: " + response.statusCode());
}
}
// Asynchronous chained processing
public static void asyncChainedRequests(HttpClient client) throws Exception {
// Initial request
HttpRequest initialRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/auth/login"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("""
{"username": "user", "password": "pass"}
"""))
.build();
// Chain processing: login → token acquisition → access protected resource
CompletableFuture<String> result = client
.sendAsync(initialRequest, HttpResponse.BodyHandlers.ofString())
.thenCompose(loginResponse -> {
if (loginResponse.statusCode() == 200) {
// Login successful, extract token (JSON parsing required in practice)
String token = extractTokenFromResponse(loginResponse.body());
// Access protected resource using token
HttpRequest protectedRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/protected-resource"))
.header("Authorization", "Bearer " + token)
.GET()
.build();
return client.sendAsync(protectedRequest, HttpResponse.BodyHandlers.ofString());
} else {
// Login failure handling
return CompletableFuture.failedFuture(
new RuntimeException("Login failed: " + loginResponse.statusCode())
);
}
})
.thenApply(protectedResponse -> {
if (protectedResponse.statusCode() == 200) {
return protectedResponse.body();
} else {
throw new RuntimeException("Protected resource access failed: " +
protectedResponse.statusCode());
}
})
.exceptionally(throwable -> {
System.err.println("Chain processing error: " + throwable.getMessage());
return "Error occurred";
});
String finalResult = result.get();
System.out.println("Chain processing result: " + finalResult);
}
private static String extractTokenFromResponse(String body) {
// In practice, parse using JSON library
// Hard-coded here for sample purposes
return "sample-jwt-token";
}
}
Authentication, Security, and Custom Configuration
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.Base64;
import javax.net.ssl.SSLContext;
public class SecurityHttpClientExample {
public static void main(String[] args) throws Exception {
// Basic authentication example
basicAuthenticationExample();
// Bearer Token authentication
bearerTokenExample();
// Proxy configuration
proxyConfigurationExample();
// SSL/TLS configuration
sslConfigurationExample();
// Custom authentication
customAuthenticationExample();
}
// Basic authentication
public static void basicAuthenticationExample() throws Exception {
HttpClient client = HttpClient.newBuilder()
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username", "password".toCharArray());
}
})
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/private"))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Basic authentication status: " + response.statusCode());
// Or manually set Basic authentication header
String auth = "username:password";
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
HttpRequest manualBasicRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/private"))
.header("Authorization", "Basic " + encodedAuth)
.GET()
.build();
HttpResponse<String> manualResponse = client.send(manualBasicRequest,
HttpResponse.BodyHandlers.ofString());
System.out.println("Manual Basic authentication status: " + manualResponse.statusCode());
}
// Bearer Token authentication
public static void bearerTokenExample() throws Exception {
HttpClient client = HttpClient.newHttpClient();
String jwtToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; // Actual JWT token
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/protected"))
.header("Authorization", "Bearer " + jwtToken)
.header("Accept", "application/json")
.header("User-Agent", "MyApp/1.0 (Java HttpClient)")
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Bearer authentication status: " + response.statusCode());
// API key authentication example
HttpRequest apiKeyRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.header("X-API-Key", "your-api-key-here")
.header("Accept", "application/json")
.GET()
.build();
HttpResponse<String> apiKeyResponse = client.send(apiKeyRequest,
HttpResponse.BodyHandlers.ofString());
System.out.println("API key authentication status: " + apiKeyResponse.statusCode());
}
// Proxy configuration
public static void proxyConfigurationExample() throws Exception {
// HTTP proxy configuration
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 8080)))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Proxy via status: " + response.statusCode());
// Authenticated proxy (combined with Authenticator)
HttpClient authProxyClient = HttpClient.newBuilder()
.proxy(ProxySelector.of(new InetSocketAddress("authenticated-proxy.example.com", 8080)))
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication("proxyuser", "proxypass".toCharArray());
}
return null;
}
})
.build();
}
// SSL/TLS configuration
public static void sslConfigurationExample() throws Exception {
// Use default SSLContext
HttpClient defaultSslClient = HttpClient.newBuilder()
.sslContext(SSLContext.getDefault())
.build();
// Custom SSLContext (example: specify specific TLS version)
try {
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, null, null);
HttpClient customSslClient = HttpClient.newBuilder()
.sslContext(sslContext)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://secure-api.example.com/data"))
.header("Accept", "application/json")
.GET()
.build();
HttpResponse<String> response = customSslClient.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Custom SSL status: " + response.statusCode());
} catch (NoSuchAlgorithmException e) {
System.err.println("TLSv1.3 not available: " + e.getMessage());
}
}
// Custom authentication and headers
public static void customAuthenticationExample() throws Exception {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
// Custom headers and timeout configuration
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://secure-api.example.com/data"))
.header("Accept", "application/json")
.header("Accept-Language", "en-US,ja-JP")
.header("X-API-Version", "v2")
.header("X-Request-ID", java.util.UUID.randomUUID().toString())
.header("Authorization", "Bearer your-jwt-token")
.header("User-Agent", "MyApp/1.0 (Java " + System.getProperty("java.version") + ")")
.timeout(Duration.ofSeconds(60))
.GET()
.build();
try {
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Custom authentication status: " + response.statusCode());
System.out.println("Response headers: " + response.headers().map());
// Cookie handling example
response.headers().allValues("Set-Cookie").forEach(cookie -> {
System.out.println("Received cookie: " + cookie);
});
} catch (Exception e) {
System.err.println("Request error: " + e.getMessage());
}
}
}
Error Handling and Retry Functionality
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpConnectTimeoutException;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpTimeoutException;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
public class ErrorHandlingHttpClientExample {
private static final HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
public static void main(String[] args) {
// Basic error handling
basicErrorHandling();
// Request with retry functionality
retryableRequest();
// Asynchronous error handling
asyncErrorHandling();
// Circuit breaker pattern
circuitBreakerExample();
}
// Basic error handling
public static void basicErrorHandling() {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.timeout(Duration.ofSeconds(30))
.GET()
.build();
try {
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
// Status code specific processing
switch (response.statusCode()) {
case 200:
System.out.println("Success: " + response.body());
break;
case 401:
System.out.println("Authentication error: Please check your token");
break;
case 403:
System.out.println("Permission error: Access denied");
break;
case 404:
System.out.println("Not found: Resource does not exist");
break;
case 429:
System.out.println("Rate limit: Please wait before retrying");
break;
case 500:
case 502:
case 503:
case 504:
System.out.println("Server error: " + response.statusCode());
break;
default:
System.out.println("Unexpected status: " + response.statusCode());
}
} catch (HttpConnectTimeoutException e) {
System.err.println("Connection timeout: " + e.getMessage());
} catch (HttpTimeoutException e) {
System.err.println("Request timeout: " + e.getMessage());
} catch (ConnectException e) {
System.err.println("Connection error: Cannot connect to server - " + e.getMessage());
} catch (SocketTimeoutException e) {
System.err.println("Socket timeout: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO error: " + e.getMessage());
} catch (InterruptedException e) {
System.err.println("Processing was interrupted: " + e.getMessage());
Thread.currentThread().interrupt(); // Restore interrupt status
} catch (Exception e) {
System.err.println("Unexpected error: " + e.getMessage());
}
}
// Request with retry functionality
public static void retryableRequest() {
String url = "https://api.example.com/unstable-endpoint";
int maxRetries = 3;
Duration baseDelay = Duration.ofMillis(500);
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(10))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("Retry success (attempt " + (attempt + 1) + "): " + response.body());
return; // Exit on success
} else if (isRetryableStatus(response.statusCode()) && attempt < maxRetries) {
System.out.println("Attempt " + (attempt + 1) + " failed (status: " +
response.statusCode() + "). Retrying...");
} else {
System.err.println("Finally failed (status: " + response.statusCode() + ")");
return;
}
} catch (Exception e) {
if (attempt < maxRetries && isRetryableException(e)) {
System.out.println("Exception in attempt " + (attempt + 1) + ": " + e.getMessage() + ". Retrying...");
} else {
System.err.println("Finally failed: " + e.getMessage());
return;
}
}
// Wait with exponential backoff
if (attempt < maxRetries) {
try {
Duration delay = baseDelay.multipliedBy((long) Math.pow(2, attempt));
// Add jitter to distribute server load
long jitter = ThreadLocalRandom.current().nextLong(0, delay.toMillis() / 4);
Thread.sleep(delay.toMillis() + jitter);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
System.err.println("Interrupted during retry wait");
return;
}
}
}
}
private static boolean isRetryableStatus(int statusCode) {
return statusCode == 429 || // Too Many Requests
statusCode == 500 || // Internal Server Error
statusCode == 502 || // Bad Gateway
statusCode == 503 || // Service Unavailable
statusCode == 504; // Gateway Timeout
}
private static boolean isRetryableException(Exception e) {
return e instanceof HttpTimeoutException ||
e instanceof HttpConnectTimeoutException ||
e instanceof ConnectException ||
e instanceof SocketTimeoutException;
}
// Asynchronous error handling
public static void asyncErrorHandling() {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/async-data"))
.timeout(Duration.ofSeconds(30))
.GET()
.build();
CompletableFuture<String> result = client
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(response -> {
if (response.statusCode() == 200) {
return response.body();
} else {
throw new RuntimeException("HTTP Error: " + response.statusCode());
}
})
.exceptionally(throwable -> {
if (throwable.getCause() instanceof HttpTimeoutException) {
System.err.println("Async request timeout");
return "TIMEOUT_ERROR";
} else if (throwable.getCause() instanceof ConnectException) {
System.err.println("Async connection error");
return "CONNECTION_ERROR";
} else {
System.err.println("Async processing error: " + throwable.getMessage());
return "UNKNOWN_ERROR";
}
})
.orTimeout(Duration.ofSeconds(45)); // Overall timeout
try {
String responseBody = result.get();
System.out.println("Async result: " + responseBody);
} catch (Exception e) {
System.err.println("Async processing exception: " + e.getMessage());
}
}
// Simple circuit breaker pattern
static class SimpleCircuitBreaker {
private int failureCount = 0;
private long lastFailureTime = 0;
private final int threshold;
private final Duration timeout;
private boolean isOpen = false;
public SimpleCircuitBreaker(int threshold, Duration timeout) {
this.threshold = threshold;
this.timeout = timeout;
}
public boolean canExecute() {
if (!isOpen) {
return true;
}
// Try reset after timeout
if (System.currentTimeMillis() - lastFailureTime > timeout.toMillis()) {
isOpen = false;
failureCount = 0;
return true;
}
return false;
}
public void recordSuccess() {
failureCount = 0;
isOpen = false;
}
public void recordFailure() {
failureCount++;
lastFailureTime = System.currentTimeMillis();
if (failureCount >= threshold) {
isOpen = true;
}
}
public boolean isOpen() {
return isOpen;
}
}
public static void circuitBreakerExample() {
SimpleCircuitBreaker circuitBreaker = new SimpleCircuitBreaker(3, Duration.ofMinutes(1));
for (int i = 0; i < 10; i++) {
if (!circuitBreaker.canExecute()) {
System.out.println("Circuit breaker is open. Skipping request.");
continue;
}
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/unreliable"))
.timeout(Duration.ofSeconds(5))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
circuitBreaker.recordSuccess();
System.out.println("Success: " + response.body());
} else {
circuitBreaker.recordFailure();
System.out.println("Failed (status: " + response.statusCode() + ")");
}
} catch (Exception e) {
circuitBreaker.recordFailure();
System.err.println("Exception occurred: " + e.getMessage());
}
// Wait a bit before next request
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
}
File Upload, Download, and Streaming
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Flow;
public class FileStreamingHttpClientExample {
private static final HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
public static void main(String[] args) throws Exception {
// File upload
fileUploadExample();
// File download
fileDownloadExample();
// Streaming upload
streamingUploadExample();
// Streaming download
streamingDownloadExample();
}
// File upload
public static void fileUploadExample() throws Exception {
Path filePath = Path.of("upload-file.txt");
// Create file for upload
Files.writeString(filePath, "This is a test file for upload.\nMultiple lines content...");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/post"))
.header("Content-Type", "text/plain; charset=UTF-8")
.header("Authorization", "Bearer your-token")
.POST(HttpRequest.BodyPublishers.ofFile(filePath))
.timeout(Duration.ofMinutes(5))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("File upload status: " + response.statusCode());
System.out.println("Response: " + response.body());
// Multipart file upload
multipartFileUpload();
}
// Multipart file upload
public static void multipartFileUpload() throws Exception {
Path filePath = Path.of("multipart-file.txt");
Files.writeString(filePath, "Multipart upload test file");
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
String multipartBody = "--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" +
"Content-Type: text/plain\r\n\r\n" +
Files.readString(filePath) + "\r\n" +
"--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"description\"\r\n\r\n" +
"Test file description\r\n" +
"--" + boundary + "--\r\n";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/post"))
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(HttpRequest.BodyPublishers.ofString(multipartBody))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Multipart upload status: " + response.statusCode());
}
// File download
public static void fileDownloadExample() throws Exception {
Path downloadPath = Path.of("downloaded-file.txt");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/bytes/1024"))
.header("Accept", "*/*")
.GET()
.build();
// Download directly to file
HttpResponse<Path> response = client.send(request,
HttpResponse.BodyHandlers.ofFile(downloadPath));
System.out.println("Download status: " + response.statusCode());
System.out.println("File saved to: " + response.body());
System.out.println("File size: " + Files.size(downloadPath) + " bytes");
// Asynchronous download
asyncFileDownload();
}
// Asynchronous file download
public static void asyncFileDownload() throws Exception {
Path asyncDownloadPath = Path.of("async-downloaded-file.dat");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/bytes/2048"))
.GET()
.build();
CompletableFuture<Path> downloadFuture = client
.sendAsync(request, HttpResponse.BodyHandlers.ofFile(asyncDownloadPath))
.thenApply(HttpResponse::body);
// Wait for download completion
Path downloadedFile = downloadFuture.get();
System.out.println("Async download completed: " + downloadedFile);
System.out.println("File size: " + Files.size(downloadedFile) + " bytes");
}
// Streaming upload
public static void streamingUploadExample() throws Exception {
// Custom BodyPublisher for streaming
HttpRequest.BodyPublisher streamingPublisher = new HttpRequest.BodyPublisher() {
@Override
public long contentLength() {
return -1; // Unknown size
}
@Override
public void subscribe(Flow.Subscriber<? super java.nio.ByteBuffer> subscriber) {
// Streaming data transmission logic
subscriber.onSubscribe(new Flow.Subscription() {
private boolean cancelled = false;
private int sentChunks = 0;
@Override
public void request(long n) {
if (cancelled) return;
try {
for (int i = 0; i < n && sentChunks < 5; i++, sentChunks++) {
String chunk = "Streaming chunk " + sentChunks + "\n";
subscriber.onNext(java.nio.ByteBuffer.wrap(chunk.getBytes()));
}
if (sentChunks >= 5) {
subscriber.onComplete();
}
} catch (Exception e) {
subscriber.onError(e);
}
}
@Override
public void cancel() {
cancelled = true;
}
});
}
};
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/post"))
.header("Content-Type", "text/plain")
.header("Transfer-Encoding", "chunked")
.POST(streamingPublisher)
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Streaming upload status: " + response.statusCode());
}
// Streaming download
public static void streamingDownloadExample() throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/stream/5"))
.GET()
.build();
// Custom BodyHandler for streaming processing
HttpResponse<Void> response = client.send(request,
HttpResponse.BodyHandlers.fromSubscriber(new Flow.Subscriber<java.util.List<java.nio.ByteBuffer>>() {
private Flow.Subscription subscription;
private int receivedChunks = 0;
@Override
public void onSubscribe(Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1); // Request first chunk
}
@Override
public void onNext(java.util.List<java.nio.ByteBuffer> buffers) {
receivedChunks++;
int totalBytes = buffers.stream().mapToInt(java.nio.ByteBuffer::remaining).sum();
System.out.println("Chunk " + receivedChunks + " received: " + totalBytes + " bytes");
// Data processing (output content in this example)
buffers.forEach(buffer -> {
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
System.out.print(new String(bytes));
});
subscription.request(1); // Request next chunk
}
@Override
public void onError(Throwable throwable) {
System.err.println("Streaming error: " + throwable.getMessage());
}
@Override
public void onComplete() {
System.out.println("\nStreaming completed (total chunks: " + receivedChunks + ")");
}
}));
System.out.println("Streaming download status: " + response.statusCode());
}
// Download with progress display
public static void downloadWithProgress() throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/bytes/10240")) // 10KB
.GET()
.build();
HttpResponse<InputStream> response = client.send(request,
HttpResponse.BodyHandlers.ofInputStream());
if (response.statusCode() == 200) {
Path outputPath = Path.of("progress-download.dat");
try (InputStream inputStream = response.body()) {
long totalSize = response.headers().firstValueAsLong("Content-Length").orElse(-1L);
long downloadedBytes = 0;
byte[] buffer = new byte[1024];
int bytesRead;
Files.deleteIfExists(outputPath);
while ((bytesRead = inputStream.read(buffer)) != -1) {
Files.write(outputPath, buffer, 0, bytesRead,
StandardOpenOption.CREATE, StandardOpenOption.APPEND);
downloadedBytes += bytesRead;
if (totalSize > 0) {
double progress = (double) downloadedBytes / totalSize * 100;
System.out.printf("\rDownload progress: %.1f%% (%d/%d bytes)",
progress, downloadedBytes, totalSize);
} else {
System.out.printf("\rDownload: %d bytes", downloadedBytes);
}
}
System.out.println("\nDownload completed: " + outputPath);
}
}
}
}