package com.rongyichuang.judge; import com.fasterxml.jackson.databind.ObjectMapper; 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.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.URI; import java.util.Collections; import java.util.Map; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") public class JudgeRealCOSUploadTest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; private final ObjectMapper objectMapper = new ObjectMapper(); @Test void testRealCOSUpload() throws Exception { System.out.println("=== 真实COS上传测试 ==="); // 步骤1: 获取真实的COS上传凭证 System.out.println("\n步骤1: 获取COS上传凭证"); Map credentials = getRealUploadCredentials(); if (credentials == null || credentials.isEmpty()) { System.out.println("❌ 无法获取上传凭证,跳过真实上传测试"); return; } System.out.println("✅ 获取到上传凭证:"); credentials.forEach((key, value) -> { if (key.contains("secret") || key.contains("token")) { System.out.println(" " + key + ": " + maskSensitiveInfo(value.toString())); } else { System.out.println(" " + key + ": " + value); } }); // 步骤2: 准备上传文件 System.out.println("\n步骤2: 准备上传文件"); File logoFile = findLogoFile(); if (logoFile == null || !logoFile.exists()) { System.out.println("❌ 找不到logo.png文件,跳过上传测试"); return; } System.out.println("✅ 找到logo文件: " + logoFile.getAbsolutePath()); System.out.println(" 文件大小: " + logoFile.length() + " bytes"); // 步骤3: 执行真实的COS上传 System.out.println("\n步骤3: 执行COS上传"); String uploadResult = performRealCOSUpload(credentials, logoFile); if (uploadResult != null) { System.out.println("✅ 文件上传成功!"); System.out.println(" 上传路径: " + uploadResult); } else { System.out.println("❌ 文件上传失败!"); } } /** * 获取真实的COS上传凭证 */ private Map getRealUploadCredentials() throws Exception { String query = """ query { getUploadCredentials { bucket region key presignedUrl expiration } } """; try { Map request = Collections.singletonMap("query", query); String response = postGraphQL(request); Map responseMap = objectMapper.readValue(response, Map.class); if (responseMap.containsKey("errors")) { System.out.println("❌ GraphQL错误: " + responseMap.get("errors")); return null; } Map data = (Map) responseMap.get("data"); return (Map) data.get("getUploadCredentials"); } catch (Exception e) { System.out.println("❌ 获取上传凭证失败: " + e.getMessage()); e.printStackTrace(); return null; } } /** * 查找logo文件 */ private File findLogoFile() { // 尝试多个可能的路径 String[] possiblePaths = { "UI/logo.png", "../UI/logo.png", "../../UI/logo.png", "UI/logo.jpg", "../UI/logo.jpg", "../../UI/logo.jpg" }; for (String path : possiblePaths) { File file = new File(path); if (file.exists()) { return file; } } return null; } /** * 执行真实的COS上传(使用预签名URL) */ private String performRealCOSUpload(Map credentials, File file) { try { // 获取预签名URL String presignedUrl = (String) credentials.get("presignedUrl"); String objectKey = (String) credentials.get("key"); if (presignedUrl == null || presignedUrl.isEmpty()) { System.out.println("❌ 预签名URL为空"); return null; } System.out.println("预签名URL: " + presignedUrl); System.out.println("对象键: " + objectKey); // 读取文件内容 byte[] fileContent = readFileContent(file); System.out.println("文件内容大小: " + fileContent.length + " bytes"); // 使用预签名URL直接上传,不需要额外的认证头部 HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(presignedUrl)) .header("Content-Type", getContentType(file)) .PUT(HttpRequest.BodyPublishers.ofByteArray(fileContent)) .build(); // 发送请求 System.out.println("发送上传请求..."); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("响应状态码: " + response.statusCode()); System.out.println("响应头: " + response.headers().map()); if (!response.body().isEmpty()) { System.out.println("响应体: " + response.body()); } if (response.statusCode() == 200 || response.statusCode() == 201 || response.statusCode() == 204) { System.out.println("✅ 文件上传成功!"); return objectKey; } else { System.out.println("❌ 上传失败,状态码: " + response.statusCode()); if (!response.body().isEmpty()) { System.out.println("❌ 响应内容: " + response.body()); } return null; } } catch (Exception e) { System.out.println("❌ COS上传异常: " + e.getMessage()); e.printStackTrace(); return null; } } /** * 读取文件内容 */ private byte[] readFileContent(File file) throws IOException { try (FileInputStream fis = new FileInputStream(file)) { return fis.readAllBytes(); } } /** * 获取文件内容类型 */ private String getContentType(File file) { String fileName = file.getName().toLowerCase(); if (fileName.endsWith(".png")) { return "image/png"; } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) { return "image/jpeg"; } else { return "application/octet-stream"; } } /** * 发送GraphQL请求 */ private String postGraphQL(Map request) throws Exception { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity> entity = new HttpEntity<>(request, headers); ResponseEntity response = restTemplate.postForEntity( "http://localhost:" + port + "/graphql", entity, String.class); if (response.getStatusCode().is2xxSuccessful()) { return response.getBody(); } else { throw new RuntimeException("GraphQL request failed with status: " + response.getStatusCode() + " and body: " + response.getBody()); } } /** * 掩码敏感信息 */ private String maskSensitiveInfo(String info) { if (info == null || info.length() <= 8) { return "****"; } return info.substring(0, 4) + "****" + info.substring(info.length() - 4); } }