diff --git a/config/application-external.yml b/config/application-external.yml index cc87555..269b0ec 100644 --- a/config/application-external.yml +++ b/config/application-external.yml @@ -48,6 +48,10 @@ ftp-sync: oss: base-url: http://53.1.211.7:8090 upload-path: voice/record/ + # OSS网络异常或5xx时最多尝试次数;4xx不重试 + upload-max-attempts: 3 + # 线性退避基础间隔:第1次失败等1秒,第2次失败等2秒 + upload-retry-delay-ms: 1000 # 地市FTP配置(每个地市独立部署一份,只配当前地市即可) cities: diff --git a/src/main/java/com/threecloud/dataserviceyy/util/FileUploadUtil.java b/src/main/java/com/threecloud/dataserviceyy/util/FileUploadUtil.java index 694da68..14fc96c 100644 --- a/src/main/java/com/threecloud/dataserviceyy/util/FileUploadUtil.java +++ b/src/main/java/com/threecloud/dataserviceyy/util/FileUploadUtil.java @@ -11,6 +11,8 @@ import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import javax.annotation.PostConstruct; @@ -36,6 +38,12 @@ public class FileUploadUtil { @Value("${ftp-sync.oss.base-url:http://53.1.211.7:8090}") private String ossBaseUrl; + @Value("${ftp-sync.oss.upload-max-attempts:3}") + private int uploadMaxAttempts; + + @Value("${ftp-sync.oss.upload-retry-delay-ms:1000}") + private long uploadRetryDelayMs; + private RestTemplate restTemplate; @PostConstruct @@ -56,6 +64,10 @@ public class FileUploadUtil { * @return 完整的文件访问URL */ public String uploadWav(String ossPath, String fileName, byte[] fileData) { + if (fileData == null || fileData.length == 0) { + throw new IllegalArgumentException("待上传录音文件为空: " + fileName); + } + String path = "voice/" + ossPath; if (fileName != null && !fileName.isEmpty() && path.endsWith(fileName)) { path = path.substring(0, path.length() - fileName.length()); @@ -65,6 +77,38 @@ public class FileUploadUtil { } logger.debug("上传文件到OSS, path={}, 大小={} bytes", path, fileData.length); + int attempts = Math.max(1, uploadMaxAttempts); + Exception lastError = null; + for (int attempt = 1; attempt <= attempts; attempt++) { + try { + return doUpload(path, fileName, fileData); + } catch (HttpClientErrorException e) { + // 4xx通常是文件大小、权限或请求参数问题,重试无意义。 + logger.error("OSS拒绝上传,不再重试: fileName={}, status={}", fileName, e.getStatusCode()); + throw new RuntimeException("文件上传失败: " + e.getMessage(), e); + } catch (Exception e) { + lastError = e; + if (attempt >= attempts) { + break; + } + long delay = Math.max(0L, uploadRetryDelayMs) * attempt; + logger.warn("OSS上传失败,准备重试: fileName={}, attempt={}/{}, delay={}ms, 原因={}", + fileName, attempt, attempts, delay, e.getMessage()); + try { + Thread.sleep(delay); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + throw new RuntimeException("OSS上传重试被中断", interrupted); + } + } + } + + logger.error("OSS上传最终失败: fileName={}, attempts={}", fileName, attempts, lastError); + throw new RuntimeException("文件上传失败: " + + (lastError == null ? "未知异常" : lastError.getMessage()), lastError); + } + + private String doUpload(String path, String fileName, byte[] fileData) { try { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); @@ -99,6 +143,7 @@ public class FileUploadUtil { Map map = JSON.parseObject(responseBody, Map.class); if (map != null && (map.containsKey("fileUrl") || map.containsKey("url"))) { String fileUrl = parseFileUrl(responseBody); + validateFileUrl(fileUrl, fileName); logger.info("文件上传完成 (扁平格式), URL: {}", fileUrl); return fileUrl; } @@ -111,6 +156,7 @@ public class FileUploadUtil { ResultEntity result = JSON.parseObject(responseBody, ResultEntity.class); if (result != null && result.getCode() != null && result.getCode() == ResultEntity.StatusCode.SUCCESS.getCode()) { String fileUrl = parseFileUrl(result.getContent()); + validateFileUrl(fileUrl, fileName); logger.info("文件上传完成 (包装格式), URL: {}", fileUrl); return fileUrl; } else { @@ -127,6 +173,12 @@ public class FileUploadUtil { } } + private void validateFileUrl(String fileUrl, String fileName) { + if (!StringUtils.hasText(fileUrl)) { + throw new RuntimeException("OSS上传成功但未返回文件地址: " + fileName); + } + } + /** * 从响应 content 中解析 fileUrl *