SpringBoot中OKHttp和壓縮文件的使用實(shí)戰(zhàn)教程
OKHttp和壓縮文件實(shí)戰(zhàn)
一、發(fā)起請(qǐng)求處理
import okhttp3.*;
import org.junit.jupiter.api.*;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Map;
public class ApiServiceCaller {
private static final ExecutorService executor = Executors.newFixedThreadPool(10, runnable -> {
Thread thread = new Thread(runnable);
thread.setName("ApiServiceCaller-Thread");
thread.setDaemon(true);
return thread;
});
private static final Logger logger = Logger.getLogger(ApiServiceCaller.class.getName());
private static final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
.retryOnConnectionFailure(true)
.build();
// 異步調(diào)用外部系統(tǒng) API 的方法
public CompletableFuture<String> callExternalApi(String url, Map<String, String> params, String method) {
return CompletableFuture.supplyAsync(() -> {
try {
Request request = buildRequest(url, params, method);
return executeRequest(request);
} catch (Exception e) {
logger.log(Level.SEVERE, "構(gòu)建請(qǐng)求或執(zhí)行請(qǐng)求時(shí)出錯(cuò)", e);
throw new RuntimeException("調(diào)用 API 時(shí)出錯(cuò): " + url, e);
}
}, executor);
}
// 構(gòu)建 GET 請(qǐng)求
private Request buildGetRequest(String url, Map<String, String> params) {
HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
if (params != null && !params.isEmpty()) {
params.forEach(httpBuilder::addQueryParameter);
}
return new Request.Builder().url(httpBuilder.build()).get().build();
}
// 構(gòu)建 POST 請(qǐng)求
private Request buildPostRequest(String url, Map<String, String> params) throws IOException {
RequestBody body = RequestBody.create(
MediaType.parse("application/json"),
new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(params)
);
return new Request.Builder().url(url).post(body).build();
}
// 通用請(qǐng)求構(gòu)建方法
private Request buildRequest(String url, Map<String, String> params, String method) throws IOException {
if ("GET".equalsIgnoreCase(method)) {
return buildGetRequest(url, params);
} else if ("POST".equalsIgnoreCase(method)) {
return buildPostRequest(url, params);
} else {
throw new IllegalArgumentException("不支持的方法: " + method);
}
}
// 執(zhí)行請(qǐng)求并處理響應(yīng)
private String executeRequest(Request request) throws IOException {
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful() && response.body() != null) {
String responseBody = response.body().string();
logger.info("收到響應(yīng): " + responseBody);
return responseBody;
} else {
logger.warning("收到非正常響應(yīng)碼: " + response.code());
throw new RuntimeException("調(diào)用 API 失敗,響應(yīng)碼: " + response.code());
}
}
}
// 處理多個(gè)不同 URL 和參數(shù)的 API 調(diào)用的方法
public List<CompletableFuture<String>> callMultipleApis(List<ApiRequest> apiRequests) {
logger.info("正在調(diào)用多個(gè) API...");
return apiRequests.stream()
.map(request -> callExternalApi(request.getUrl(), request.getParams(), request.getMethod()))
.collect(Collectors.toList());
}
// 高效處理 CompletableFuture 結(jié)果的方法
public void processApiResponses(List<CompletableFuture<String>> futures) {
CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
allOf.thenAccept(v -> futures.forEach(future -> {
future.handle((response, throwable) -> {
if (throwable != null) {
logger.log(Level.SEVERE, "處理 future 出錯(cuò)", throwable);
System.err.println("處理 future 出錯(cuò): " + throwable.getMessage());
} else {
logger.info("處理響應(yīng): " + response);
System.out.println(response);
}
return null;
});
}));
}
// 主函數(shù),調(diào)用 API
public static void main(String[] args) {
ApiServiceCaller apiServiceCaller = new ApiServiceCaller();
List<ApiRequest> apiRequests = new ArrayList<>();
apiRequests.add(new ApiRequest("http://example.com/api1", Map.of("param1", "value1"), "GET"));
apiRequests.add(new ApiRequest("http://example.com/api2", Map.of("key", "value"), "POST"));
apiRequests.add(new ApiRequest("http://example.com/api3", Map.of("param3", "value3"), "GET"));
logger.info("開始調(diào)用 API...");
List<CompletableFuture<String>> apiCalls = apiServiceCaller.callMultipleApis(apiRequests);
apiServiceCaller.processApiResponses(apiCalls);
}
// ApiServiceCaller 的單元測(cè)試
public static class ApiServiceCallerTest {
@Test
public void testCallExternalApi_getRequest() {
ApiServiceCaller caller = new ApiServiceCaller();
CompletableFuture<String> responseFuture = caller.callExternalApi("http://example.com/api1", Map.of("param", "value"), "GET");
Assertions.assertDoesNotThrow(() -> {
String response = responseFuture.get(10, TimeUnit.SECONDS);
Assertions.assertNotNull(response);
});
}
@Test
public void testCallExternalApi_postRequest() {
ApiServiceCaller caller = new ApiServiceCaller();
CompletableFuture<String> responseFuture = caller.callExternalApi("http://example.com/api1", Map.of("key", "value"), "POST");
Assertions.assertDoesNotThrow(() -> {
String response = responseFuture.get(10, TimeUnit.SECONDS);
Assertions.assertNotNull(response);
});
}
@Test
public void testCallMultipleApis() {
ApiServiceCaller caller = new ApiServiceCaller();
List<ApiRequest> apiRequests = new ArrayList<>();
apiRequests.add(new ApiRequest("http://example.com/api1", Map.of("param1", "value1"), "GET"));
apiRequests.add(new ApiRequest("http://example.com/api2", Map.of("key", "value"), "POST"));
List<CompletableFuture<String>> responseFutures = caller.callMultipleApis(apiRequests);
Assertions.assertEquals(2, responseFutures.size());
responseFutures.forEach(future -> Assertions.assertDoesNotThrow(() -> {
String response = future.get(10, TimeUnit.SECONDS);
Assertions.assertNotNull(response);
}));
}
}
// 用于保存 API 請(qǐng)求詳情的類
public static class ApiRequest {
private final String url;
private final Map<String, String> params;
private final String method;
public ApiRequest(String url, Map<String, String> params, String method) {
this.url = url;
this.params = params;
this.method = method;
}
public String getUrl() {
return url;
}
public Map<String, String> getParams() {
return params;
}
public String getMethod() {
return method;
}
}
}
// 確保執(zhí)行器的優(yōu)雅關(guān)閉
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
logger.info("正在關(guān)閉執(zhí)行器...");
executor.shutdown();
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
logger.warning("執(zhí)行器未在指定時(shí)間內(nèi)終止。");
executor.shutdownNow();
}
} catch (InterruptedException e) {
logger.log(Level.SEVERE, "關(guān)閉過(guò)程中斷", e);
executor.shutdownNow();
}
}));二、壓縮文件
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import java.io.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.util.concurrent.TimeUnit;
public class S3DownloadAndCompress {
private final AmazonS3 s3Client;
private final ExecutorService executorService;
public S3DownloadAndCompress(int threadPoolSize) {
System.out.println("初始化 S3 客戶端和執(zhí)行器服務(wù)...");
this.s3Client = AmazonS3ClientBuilder.standard().build();
this.executorService = Executors.newFixedThreadPool(threadPoolSize);
}
public ByteArrayOutputStream getCompressedFileStream(List<String> fileKeys, String bucketName) {
System.out.println("開始下載和壓縮過(guò)程...");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zipOut = new ZipOutputStream(baos)) {
List<CompletableFuture<Void>> futures = fileKeys.stream()
.map(fileKey -> CompletableFuture.runAsync(() -> {
System.out.println("開始下載和壓縮文件: " + fileKey);
downloadAndCompressFile(s3Client, bucketName, fileKey, zipOut);
System.out.println("完成下載和壓縮文件: " + fileKey);
}, executorService))
.collect(Collectors.toList());
CompletableFuture<Void> allDownloads = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
allDownloads.join();
System.out.println("所有文件已成功下載和壓縮。");
} catch (IOException e) {
System.err.println("下載和壓縮過(guò)程中出錯(cuò): " + e.getMessage());
e.printStackTrace();
} finally {
System.out.println("關(guān)閉執(zhí)行器服務(wù)...");
executorService.shutdown();
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
System.out.println("執(zhí)行器服務(wù)未能在60秒內(nèi)終止,正在強(qiáng)制關(guān)閉...");
executorService.shutdownNow();
}
} catch (InterruptedException e) {
System.out.println("等待執(zhí)行器服務(wù)終止時(shí)被中斷,強(qiáng)制關(guān)閉...");
executorService.shutdownNow();
}
}
if (baos.size() == 0) {
System.out.println("壓縮文件流為空。");
}
return baos;
}
public void saveCompressedFileToPath(ByteArrayOutputStream compressedStream, String targetPath) {
if (compressedStream == null || compressedStream.size() == 0) {
System.out.println("壓縮文件流為空,無(wú)法保存。");
return;
}
try (FileOutputStream fos = new FileOutputStream(targetPath)) {
compressedStream.writeTo(fos);
System.out.println("壓縮文件已保存到: " + targetPath);
} catch (IOException e) {
System.err.println("保存壓縮文件時(shí)出錯(cuò): " + e.getMessage());
e.printStackTrace();
}
}
private void downloadAndCompressFile(AmazonS3 s3Client, String bucketName, String fileKey, ZipOutputStream zipOut) {
synchronized (zipOut) {
try (S3Object s3Object = s3Client.getObject(bucketName, fileKey);
S3ObjectInputStream s3is = s3Object.getObjectContent()) {
System.out.println("從桶中下載文件: " + fileKey + " 桶名稱: " + bucketName);
ZipEntry zipEntry = new ZipEntry(fileKey);
zipOut.putNextEntry(zipEntry);
byte[] buffer = new byte[4096];
int length;
while ((length = s3is.read(buffer)) >= 0) {
zipOut.write(buffer, 0, length);
}
zipOut.closeEntry();
System.out.println("文件 " + fileKey + " 已添加到 zip 中。");
} catch (IOException e) {
System.err.println("下載或壓縮文件時(shí)出錯(cuò): " + fileKey + " - " + e.getMessage());
e.printStackTrace();
}
}
}
public static void main(String[] args) {
System.out.println("啟動(dòng) S3DownloadAndCompress...");
int threadPoolSize = 10; // 這個(gè)可以根據(jù)需要進(jìn)行配置
S3DownloadAndCompress downloader = new S3DownloadAndCompress(threadPoolSize);
List<String> fileKeys = List.of("file1.txt", "file2.txt", "file3.txt");
String bucketName = "your-bucket-name";
String targetPath = "compressed_files.zip";
ByteArrayOutputStream compressedStream = downloader.getCompressedFileStream(fileKeys, bucketName);
downloader.saveCompressedFileToPath(compressedStream, targetPath);
System.out.println("S3DownloadAndCompress 完成。");
}
}import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.TransferManagerBuilder;
import com.amazonaws.services.s3.transfer.Download;
import com.amazonaws.HttpMethod;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.util.concurrent.TimeUnit;
public class S3DownloadAndCompress {
private final AmazonS3 s3Client;
private final ExecutorService executorService;
private final TransferManager transferManager;
private final String defaultFileName = "default_filename.txt";
// 初始化 Amazon S3 客戶端和線程池
public S3DownloadAndCompress(int threadPoolSize) {
System.out.println("初始化 S3 客戶端和執(zhí)行器服務(wù)...");
this.s3Client = AmazonS3ClientBuilder.standard().build();
this.executorService = Executors.newFixedThreadPool(threadPoolSize);
this.transferManager = TransferManagerBuilder.standard().withS3Client(s3Client).build();
System.out.println("S3 客戶端和執(zhí)行器服務(wù)初始化完成。");
}
// 獲取文件列表,壓縮成 Zip 文件,并返回壓縮后的文件流
public ByteArrayOutputStream getCompressedFileStream(List<String> fileKeys, String bucketName) {
System.out.println("開始下載和壓縮過(guò)程...");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zipOut = new ZipOutputStream(baos)) {
List<CompletableFuture<Void>> futures = fileKeys.stream()
.map(fileKey -> CompletableFuture.runAsync(() -> {
System.out.println("開始下載和壓縮文件: " + fileKey);
downloadAndCompressFile(bucketName, fileKey, zipOut);
System.out.println("完成下載和壓縮文件: " + fileKey);
}, executorService))
.collect(Collectors.toList());
CompletableFuture<Void> allDownloads = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
allDownloads.join();
System.out.println("所有文件已成功下載和壓縮。");
} catch (IOException e) {
System.err.println("下載和壓縮過(guò)程中出錯(cuò): " + e.getMessage());
e.printStackTrace();
} finally {
shutdownExecutorService();
}
System.out.println("壓縮過(guò)程完成,返回壓縮文件流。");
return baos;
}
// 將壓縮后的文件流保存到指定路徑
public void saveCompressedFileToPath(ByteArrayOutputStream compressedStream, String targetPath) {
if (compressedStream == null || compressedStream.size() == 0) {
throw new IllegalArgumentException("壓縮文件流為空,無(wú)法保存。");
}
System.out.println("開始將壓縮文件保存到路徑: " + targetPath);
try (FileOutputStream fos = new FileOutputStream(targetPath)) {
compressedStream.writeTo(fos);
System.out.println("壓縮文件已保存到: " + targetPath);
} catch (IOException e) {
System.err.println("保存壓縮文件時(shí)出錯(cuò): " + e.getMessage());
e.printStackTrace();
}
}
// 從 S3 下載指定文件并保存到目標(biāo)路徑
public void downloadFileToPath(String bucketName, String fileKey, String targetPath) {
System.out.println("開始從 S3 下載文件: " + fileKey + " 到路徑: " + targetPath);
try {
String resolvedFileKey = resolveFileKey(bucketName, fileKey);
File targetFile = new File(targetPath);
Download download = transferManager.download(bucketName, resolvedFileKey, targetFile);
download.waitForCompletion();
System.out.println("文件已成功下載到: " + targetPath);
} catch (Exception e) {
System.err.println("下載文件時(shí)出錯(cuò): " + e.getMessage());
e.printStackTrace();
}
}
// 生成指定文件的臨時(shí)訪問(wèn)鏈接
public URL generatePresignedUrl(String bucketName, String fileKey, int expirationMinutes) {
System.out.println("生成臨時(shí)鏈接,文件: " + fileKey + " 有效期: " + expirationMinutes + " 分鐘");
try {
String resolvedFileKey = resolveFileKey(bucketName, fileKey);
Date expiration = new Date(System.currentTimeMillis() + expirationMinutes * 60 * 1000);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, resolvedFileKey)
.withMethod(HttpMethod.GET)
.withExpiration(expiration);
URL url = s3Client.generatePresignedUrl(request);
System.out.println("生成的臨時(shí)鏈接: " + url.toString());
return url;
} catch (Exception e) {
System.err.println("生成臨時(shí)鏈接時(shí)出錯(cuò): " + e.getMessage());
e.printStackTrace();
return null;
}
}
// 使用臨時(shí)鏈接下載文件并保存到指定路徑
public void downloadFileFromPresignedUrl(URL presignedUrl, String targetPath) {
System.out.println("使用臨時(shí)鏈接下載文件到路徑: " + targetPath);
try (BufferedInputStream in = new BufferedInputStream(presignedUrl.openStream());
FileOutputStream fileOutputStream = new FileOutputStream(targetPath)) {
byte[] dataBuffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 8192)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
System.out.println("文件已通過(guò)臨時(shí)鏈接成功下載到: " + targetPath);
} catch (IOException e) {
System.err.println("通過(guò)臨時(shí)鏈接下載文件時(shí)出錯(cuò): " + e.getMessage());
e.printStackTrace();
}
}
// 使用臨時(shí)鏈接獲取文件的輸入流
public InputStream getFileStreamFromPresignedUrl(URL presignedUrl) {
System.out.println("通過(guò)臨時(shí)鏈接獲取文件流: " + presignedUrl);
try {
HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
connection.setRequestMethod("GET");
InputStream inputStream = connection.getInputStream();
System.out.println("成功獲取文件流。");
return inputStream;
} catch (IOException e) {
System.err.println("通過(guò)臨時(shí)鏈接獲取文件流時(shí)出錯(cuò): " + e.getMessage());
e.printStackTrace();
return null;
}
}
// 解析文件鍵名,如果文件不存在則返回默認(rèn)文件名
private String resolveFileKey(String bucketName, String fileKey) {
System.out.println("解析文件鍵名: " + fileKey);
if (s3Client.doesObjectExist(bucketName, fileKey)) {
System.out.println("文件存在: " + fileKey);
return fileKey;
} else {
System.out.println("文件不存在,使用默認(rèn)文件名: " + defaultFileName);
return defaultFileName;
}
}
// 從 S3 下載文件并將其壓縮到 ZipOutputStream 中
private void downloadAndCompressFile(String bucketName, String fileKey, ZipOutputStream zipOut) {
System.out.println("從 S3 下載并壓縮文件: " + fileKey);
synchronized (zipOut) {
try (S3Object s3Object = s3Client.getObject(bucketName, fileKey);
S3ObjectInputStream s3is = s3Object.getObjectContent()) {
System.out.println("從桶中下載文件: " + fileKey + " 桶名稱: " + bucketName);
ZipEntry zipEntry = new ZipEntry(fileKey);
zipOut.putNextEntry(zipEntry);
byte[] buffer = new byte[8192];
int length;
while ((length = s3is.read(buffer)) >= 0) {
zipOut.write(buffer, 0, length);
}
zipOut.closeEntry();
System.out.println("文件 " + fileKey + " 已添加到 zip 中。");
} catch (IOException e) {
System.err.println("下載或壓縮文件時(shí)出錯(cuò): " + fileKey + " - " + e.getMessage());
e.printStackTrace();
}
}
}
// 關(guān)閉執(zhí)行器服務(wù)
private void shutdownExecutorService() {
System.out.println("關(guān)閉執(zhí)行器服務(wù)...");
try {
executorService.shutdown();
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
System.out.println("執(zhí)行器服務(wù)未能在60秒內(nèi)終止,正在強(qiáng)制關(guān)閉...");
executorService.shutdownNow();
System.out.println("已調(diào)用 shutdownNow() 強(qiáng)制關(guān)閉執(zhí)行器服務(wù)。");
}
} catch (InterruptedException e) {
System.out.println("等待執(zhí)行器服務(wù)終止時(shí)被中斷,強(qiáng)制關(guān)閉...");
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println("執(zhí)行器服務(wù)已關(guān)閉。");
}
public static void main(String[] args) {
System.out.println("啟動(dòng) S3DownloadAndCompress...");
int threadPoolSize = 10; // 這個(gè)可以根據(jù)需要進(jìn)行配置
S3DownloadAndCompress downloader = new S3DownloadAndCompress(threadPoolSize);
List<String> fileKeys = List.of("file1.txt", "file2.txt", "file3.txt");
String bucketName = "your-bucket-name";
String targetPath = "compressed_files.zip";
// 下載并壓縮文件并保存到目標(biāo)路徑
System.out.println("開始下載并壓縮文件...");
downloader.downloadAndCompressFileToPath(fileKeys, bucketName, targetPath);
System.out.println("下載并壓縮文件完成。");
// 直接下載到指定路徑
System.out.println("開始直接下載文件...");
downloader.downloadFileToPath(bucketName, "file1.txt", "downloaded_file1.txt");
System.out.println("直接下載文件完成。");
// 生成臨時(shí)鏈接
System.out.println("開始生成臨時(shí)鏈接...");
URL presignedUrl = downloader.generatePresignedUrl(bucketName, "file2.txt", 60);
if (presignedUrl != null) {
System.out.println("訪問(wèn)臨時(shí)鏈接: " + presignedUrl);
// 通過(guò)臨時(shí)鏈接下載到本地
System.out.println("通過(guò)臨時(shí)鏈接下載文件...");
downloader.downloadFileFromPresignedUrl(presignedUrl, "downloaded_from_presigned_url.txt");
System.out.println("通過(guò)臨時(shí)鏈接下載文件完成。");
// 獲取文件流
System.out.println("獲取文件流...");
InputStream fileStream = downloader.getFileStreamFromPresignedUrl(presignedUrl);
if (fileStream != null) {
System.out.println("成功獲取文件流。");
}
}
System.out.println("S3DownloadAndCompress 完成。");
}
}三、文件存儲(chǔ)
1. 配置
# Bucket 1 Configuration aws.buckets.bucket1.accessKey=accessKey1 aws.buckets.bucket1.secretKey=secretKey1 aws.buckets.bucket1.endpoint=http://endpoint1 aws.buckets.bucket1.region=us-east-1 # Bucket 2 Configuration aws.buckets.bucket2.accessKey=accessKey2 aws.buckets.bucket2.secretKey=secretKey2 aws.buckets.bucket2.endpoint=http://endpoint2 aws.buckets.bucket2.region=us-west-1
2. 實(shí)體類
package com.example.s3config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties
public class BucketConfig {
private String accessKey;
private String secretKey;
private String endpoint;
private String region;
// Getters and setters
public String getAccessKey() {
return accessKey;
}
public void setAccessKey(String accessKey) {
this.accessKey = accessKey;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
}3. 配置類
package com.example.s3config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
@Component
@ConfigurationProperties(prefix = "aws.buckets")
public class BucketsConfig {
private static final Logger logger = LoggerFactory.getLogger(BucketsConfig.class);
private Map<String, BucketConfig> bucketConfigs = new HashMap<>();
public Map<String, BucketConfig> getBucketConfigs() {
return bucketConfigs;
}
public void setBucketConfigs(Map<String, BucketConfig> bucketConfigs) {
this.bucketConfigs = bucketConfigs;
// Log to confirm if configurations are loaded correctly
logger.info("Bucket configurations loaded: {}", bucketConfigs.keySet());
}
public BucketConfig getBucketConfig(String bucketName) {
BucketConfig bucketConfig = bucketConfigs.get(bucketName);
if (bucketConfig == null) {
throw new IllegalArgumentException("Invalid bucket name: " + bucketName);
}
return bucketConfig;
}
}4. 初始化類
package com.example.s3config;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class AmazonS3Config {
private static final Logger logger = LoggerFactory.getLogger(AmazonS3Config.class);
private final BucketsConfig bucketsConfig;
private final Map<String, AmazonS3> amazonS3ClientsCache = new ConcurrentHashMap<>();
@Autowired
public AmazonS3Config(BucketsConfig bucketsConfig) {
this.bucketsConfig = bucketsConfig;
logger.info("AmazonS3Config initialized with BucketsConfig");
}
public AmazonS3 getAmazonS3Client(String bucketName) {
// Check if client is already in cache
if (amazonS3ClientsCache.containsKey(bucketName)) {
logger.debug("Returning cached AmazonS3 client for bucket: {}", bucketName);
return amazonS3ClientsCache.get(bucketName);
}
// Get bucket configuration
BucketConfig bucketConfig = bucketsConfig.getBucketConfig(bucketName);
// Ensure all required configurations are present
if (bucketConfig.getAccessKey() == null || bucketConfig.getSecretKey() == null ||
bucketConfig.getEndpoint() == null || bucketConfig.getRegion() == null) {
throw new IllegalArgumentException("Incomplete bucket configuration for: " + bucketName);
}
// Initialize AmazonS3 client
BasicAWSCredentials awsCreds = new BasicAWSCredentials(bucketConfig.getAccessKey(), bucketConfig.getSecretKey());
AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.withEndpointConfiguration(
new AmazonS3ClientBuilder.EndpointConfiguration(bucketConfig.getEndpoint(), bucketConfig.getRegion()))
.withPathStyleAccessEnabled(true)
.build();
// Cache the client for future use
amazonS3ClientsCache.put(bucketName, amazonS3);
logger.info("AmazonS3 client created and cached for bucket: {}", bucketName);
return amazonS3;
}
}5. 獲取對(duì)象
package com.example.s3config;
import com.amazonaws.services.s3.AmazonS3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
@Service
public class S3Service {
private static final Logger logger = LoggerFactory.getLogger(S3Service.class);
private final AmazonS3Config amazonS3Config;
@Autowired
public S3Service(AmazonS3Config amazonS3Config) {
this.amazonS3Config = amazonS3Config;
logger.info("S3Service initialized with AmazonS3Config");
}
public void uploadFile(String bucketName, String key, File file) {
AmazonS3 amazonS3 = amazonS3Config.getAmazonS3Client(bucketName);
amazonS3.putObject(bucketName, key, file);
logger.info("File uploaded to bucket: {}, key: {}", bucketName, key);
}
// Other operations
}6. 主程序
package com.example.s3config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableConfigurationProperties(BucketsConfig.class)
public class YourApplication {
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
@Bean
CommandLineRunner validateBucketsConfig(BucketsConfig bucketsConfig) {
return args -> {
System.out.println("Validating bucket configurations: " + bucketsConfig.getBucketConfigs().keySet());
};
}
}7. 測(cè)試類
package com.example.s3config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@TestPropertySource("classpath:application.properties")
public class BucketsConfigTest {
@Autowired
private BucketsConfig bucketsConfig;
@Test
public void testBucketsConfigLoaded() {
assertNotNull(bucketsConfig, "BucketsConfig should not be null");
assertFalse(bucketsConfig.getBucketConfigs().isEmpty(), "Bucket configurations should not be empty");
assertTrue(bucketsConfig.getBucketConfigs().containsKey("bucket1"), "Bucket1 should be present in the configurations");
assertTrue(bucketsConfig.getBucketConfigs().containsKey("bucket2"), "Bucket2 should be present in the configurations");
}
@Test
public void testGetBucketConfig() {
BucketConfig bucket1 = bucketsConfig.getBucketConfig("bucket1");
assertNotNull(bucket1, "BucketConfig for bucket1 should not be null");
assertEquals("accessKey1", bucket1.getAccessKey());
assertEquals("secretKey1", bucket1.getSecretKey());
assertEquals("http://endpoint1", bucket1.getEndpoint());
assertEquals("us-east-1", bucket1.getRegion());
}
@Test
public void testInvalidBucket() {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
bucketsConfig.getBucketConfig("invalidBucket");
});
assertEquals("Invalid bucket name: invalidBucket", exception.getMessage());
}
}package com.example.s3config;
import com.amazonaws.services.s3.AmazonS3;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@SpringBootTest
@TestPropertySource("classpath:application.properties")
public class AmazonS3ConfigTest {
@Autowired
private AmazonS3Config amazonS3Config;
@MockBean
private BucketsConfig bucketsConfig;
@Test
public void testGetAmazonS3Client() {
// Mock the BucketConfig
BucketConfig bucketConfig = new BucketConfig();
bucketConfig.setAccessKey("accessKey1");
bucketConfig.setSecretKey("secretKey1");
bucketConfig.setEndpoint("http://endpoint1");
bucketConfig.setRegion("us-east-1");
when(bucketsConfig.getBucketConfig("bucket1")).thenReturn(bucketConfig);
AmazonS3 s3Client = amazonS3Config.getAmazonS3Client("bucket1");
assertNotNull(s3Client, "AmazonS3 client should not be null");
// Verify that the client is cached
AmazonS3 cachedClient = amazonS3Config.getAmazonS3Client("bucket1");
assertSame(s3Client, cachedClient, "Cached client should be the same instance");
}
@Test
public void testGetAmazonS3ClientInvalidBucket() {
when(bucketsConfig.getBucketConfig("invalidBucket"))
.thenThrow(new IllegalArgumentException("Invalid bucket name: invalidBucket"));
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
amazonS3Config.getAmazonS3Client("invalidBucket");
});
assertEquals("Invalid bucket name: invalidBucket", exception.getMessage());
}
}package com.example.s3config;
import com.amazonaws.services.s3.AmazonS3;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.File;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class S3ServiceTest {
@Mock
private AmazonS3Config amazonS3Config;
@Mock
private AmazonS3 amazonS3;
@InjectMocks
private S3Service s3Service;
@Test
public void testUploadFile() {
String bucketName = "bucket1";
String key = "testFile.txt";
File file = new File("testFile.txt");
when(amazonS3Config.getAmazonS3Client(bucketName)).thenReturn(amazonS3);
s3Service.uploadFile(bucketName, key, file);
verify(amazonS3Config, times(1)).getAmazonS3Client(bucketName);
verify(amazonS3, times(1)).putObject(bucketName, key, file);
}
@Test
public void testUploadFileWithInvalidBucket() {
String bucketName = "invalidBucket";
String key = "testFile.txt";
File file = new File("testFile.txt");
when(amazonS3Config.getAmazonS3Client(bucketName))
.thenThrow(new IllegalArgumentException("Invalid bucket name: " + bucketName));
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
s3Service.uploadFile(bucketName, key, file);
});
assertEquals("Invalid bucket name: " + bucketName, exception.getMessage());
}
}8.依賴
確保在 pom.xml 中添加以下依賴:
<!-- AWS SDK -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.12.100</version>
</dependency>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot Configuration Processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>到此這篇關(guān)于SpringBoot中OKHttp和壓縮文件的使用的文章就介紹到這了,更多相關(guān)SpringBoot使用OKHttp內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Minio與SpringBoot使用okhttp3問(wèn)題解決
- SpringBoot Java后端實(shí)現(xiàn)okhttp3超時(shí)設(shè)置的方法實(shí)例
- 基于springboot的RestTemplate、okhttp和HttpClient對(duì)比分析
- Android使用OKhttp3實(shí)現(xiàn)登錄注冊(cè)功能+springboot搭建后端的詳細(xì)過(guò)程
- Android的簡(jiǎn)單前后端交互(okHttp+springboot+mysql)
- SpringBoot 配置 okhttp3的操作
- 使用SpringBoot+OkHttp+fastjson實(shí)現(xiàn)Github的OAuth第三方登錄
相關(guān)文章
Java中時(shí)間戳的獲取和轉(zhuǎn)換的示例分析
這篇文章主要介紹了Java中時(shí)間戳的獲取和轉(zhuǎn)換的示例分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Spring實(shí)戰(zhàn)之獲取方法返回值操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之獲取方法返回值操作,涉及spring配置文件與方法返回值操作相關(guān)使用技巧,需要的朋友可以參考下2019-12-12
SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的三種方式
優(yōu)雅停機(jī)(Graceful?Shutdown)是指應(yīng)用在接收到停止信號(hào)后,能夠妥善處理現(xiàn)有請(qǐng)求、釋放資源,然后再退出的過(guò)程,本文將詳細(xì)介紹SpringBoot中實(shí)現(xiàn)優(yōu)雅停機(jī)的三種方式,需要的朋友可以參考下2025-04-04
Java深入淺出掌握SpringBoot之MVC自動(dòng)配置原理篇
在進(jìn)行項(xiàng)目編寫前,我們還需要知道一個(gè)東西,就是SpringBoot對(duì)我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們?cè)谥笫褂貌艜?huì)更加得心應(yīng)手2021-10-10
用java實(shí)現(xiàn)斗地主發(fā)牌過(guò)程
這篇文章主要為大家詳細(xì)介紹了用java實(shí)現(xiàn)斗地主發(fā)牌過(guò)程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
java實(shí)現(xiàn)裝飾器模式(Decorator Pattern)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)裝飾器模式Decorator Pattern,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10
解決kafka:org.apache.kafka.common.errors.TimeoutException問(wèn)題
這篇文章主要介紹了解決kafka:org.apache.kafka.common.errors.TimeoutException問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01

