From e7dbdc2ef317b77a3ff4f4fbcd1f3ff3376f4df5 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Sat, 16 May 2026 02:24:04 +0800 Subject: [PATCH] fix(update): use curl for Xiaomi APK upload to bypass server-side body timeout RestTemplate drops large multipart payloads on the MI API server; switching to ProcessBuilder curl with Expect:100-continue headers and a 130-minute timeout resolves upload failures for large APKs. Co-Authored-By: Claude Sonnet 4.6 --- .../service/StoreSubmissionService.java | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/update-service/src/main/java/com/xuqm/update/service/StoreSubmissionService.java b/update-service/src/main/java/com/xuqm/update/service/StoreSubmissionService.java index 32d1a38..2ee2ebf 100644 --- a/update-service/src/main/java/com/xuqm/update/service/StoreSubmissionService.java +++ b/update-service/src/main/java/com/xuqm/update/service/StoreSubmissionService.java @@ -974,18 +974,30 @@ public class StoreSubmissionService { Map.of("name", "apk", "hash", md5Hex(apkFile)) )); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("apk", new FileSystemResource(apkFile)); - body.add("RequestData", asJsonString(requestData)); - body.add("SIG", rsaEncryptHexChunked(asJsonString(sig), publicKey)); - - ResponseEntity response = rest.postForEntity( - "https://api.developer.xiaomi.com/devupload/dev/push", - new HttpEntity<>(body, headers), - String.class); - JsonNode root = mapper.readTree(Objects.requireNonNull(response.getBody())); + // Use curl ProcessBuilder: MI API server drops large multipart body with RestTemplate. + // curl sends Expect:100-continue which bypasses the server-side body timeout. + String requestDataJson = asJsonString(requestData); + String sigJson = rsaEncryptHexChunked(asJsonString(sig), publicKey); + ProcessBuilder pb = new ProcessBuilder( + "curl", "-s", "--connect-timeout", "30", + "--max-time", String.valueOf(130 * 60), + "-F", "apk=@" + apkFile.getAbsolutePath(), + "-F", "RequestData=" + requestDataJson, + "-F", "SIG=" + sigJson, + "https://api.developer.xiaomi.com/devupload/dev/push" + ); + pb.redirectErrorStream(true); + Process process = pb.start(); + String responseBody = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8).trim(); + boolean completed = process.waitFor(130, java.util.concurrent.TimeUnit.MINUTES); + if (!completed) { + process.destroyForcibly(); + throw new IllegalStateException("curl upload to MI timed out"); + } + if (responseBody.isEmpty()) { + throw new IllegalStateException("curl upload to MI returned empty response (exit=" + process.exitValue() + ")"); + } + JsonNode root = mapper.readTree(responseBody); miCheckSuccess(root, "上传Apk"); }