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("開(kāi)始調(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("開(kāi)始下載和壓縮過(guò)程..."); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zipOut = new ZipOutputStream(baos)) { List<CompletableFuture<Void>> futures = fileKeys.stream() .map(fileKey -> CompletableFuture.runAsync(() -> { System.out.println("開(kāi)始下載和壓縮文件: " + 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("開(kāi)始下載和壓縮過(guò)程..."); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zipOut = new ZipOutputStream(baos)) { List<CompletableFuture<Void>> futures = fileKeys.stream() .map(fileKey -> CompletableFuture.runAsync(() -> { System.out.println("開(kāi)始下載和壓縮文件: " + 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("開(kāi)始將壓縮文件保存到路徑: " + 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("開(kāi)始從 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("開(kāi)始下載并壓縮文件..."); downloader.downloadAndCompressFileToPath(fileKeys, bucketName, targetPath); System.out.println("下載并壓縮文件完成。"); // 直接下載到指定路徑 System.out.println("開(kāi)始直接下載文件..."); downloader.downloadFileToPath(bucketName, "file1.txt", "downloaded_file1.txt"); System.out.println("直接下載文件完成。"); // 生成臨時(shí)鏈接 System.out.println("開(kāi)始生成臨時(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)文章
mybatis連接數(shù)據(jù)庫(kù)實(shí)現(xiàn)雙表查詢
本文主要介紹了mybatis連接數(shù)據(jù)庫(kù)實(shí)現(xiàn)雙表查詢,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-09-09spring事務(wù)隔離級(jí)別、傳播機(jī)制以及簡(jiǎn)單配置方式
這篇文章主要介紹了spring事務(wù)隔離級(jí)別、傳播機(jī)制以及簡(jiǎn)單配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01Java編程倒計(jì)時(shí)實(shí)現(xiàn)方法示例
這篇文章主要介紹了Java編程倒計(jì)時(shí)實(shí)現(xiàn)的三個(gè)示例,三種實(shí)現(xiàn)方法,具有一定參考價(jià)值,需要的朋友可以了解下。2017-09-09springboot項(xiàng)目引入外部jar包的詳細(xì)圖文教程
在項(xiàng)目中有時(shí)候需要引入外部jar包,啟動(dòng)運(yùn)行,下面這篇文章主要給大家介紹了關(guān)于springboot項(xiàng)目引入外部jar包的詳細(xì)圖文教程,需要的朋友可以參考下2023-09-09Mybatis注解開(kāi)發(fā)@Select執(zhí)行參數(shù)和執(zhí)行sql語(yǔ)句的方式(最新詳解)
@Select 是 Mybatis 框架中的一個(gè)注解,用于執(zhí)行 SQL 查詢語(yǔ)句,并把查詢結(jié)果映射到指定的 Java 對(duì)象中,這篇文章主要介紹了Mybatis注解開(kāi)發(fā)@Select執(zhí)行參數(shù)和執(zhí)行sql語(yǔ)句的方式,需要的朋友可以參考下2023-07-07java實(shí)現(xiàn)Socket通信之單線程服務(wù)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)Socket通信的單線程服務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07java 實(shí)現(xiàn)字節(jié)流和字節(jié)緩沖流讀寫(xiě)文件時(shí)間對(duì)比
這篇文章主要介紹了java 實(shí)現(xiàn)字節(jié)流和字節(jié)緩沖流讀寫(xiě)文件時(shí)間對(duì)比,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01