Browse Source

语音上传优化

ftpmain
wang 4 weeks ago
parent
commit
414e176e85
  1. 4
      config/application-external.yml
  2. 52
      src/main/java/com/threecloud/dataserviceyy/util/FileUploadUtil.java

4
config/application-external.yml

@ -48,6 +48,10 @@ ftp-sync:
oss: oss:
base-url: http://53.1.211.7:8090 base-url: http://53.1.211.7:8090
upload-path: voice/record/ upload-path: voice/record/
# OSS网络异常或5xx时最多尝试次数;4xx不重试
upload-max-attempts: 3
# 线性退避基础间隔:第1次失败等1秒,第2次失败等2秒
upload-retry-delay-ms: 1000
# 地市FTP配置(每个地市独立部署一份,只配当前地市即可) # 地市FTP配置(每个地市独立部署一份,只配当前地市即可)
cities: cities:

52
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.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -36,6 +38,12 @@ public class FileUploadUtil {
@Value("${ftp-sync.oss.base-url:http://53.1.211.7:8090}") @Value("${ftp-sync.oss.base-url:http://53.1.211.7:8090}")
private String ossBaseUrl; 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; private RestTemplate restTemplate;
@PostConstruct @PostConstruct
@ -56,6 +64,10 @@ public class FileUploadUtil {
* @return 完整的文件访问URL * @return 完整的文件访问URL
*/ */
public String uploadWav(String ossPath, String fileName, byte[] fileData) { public String uploadWav(String ossPath, String fileName, byte[] fileData) {
if (fileData == null || fileData.length == 0) {
throw new IllegalArgumentException("待上传录音文件为空: " + fileName);
}
String path = "voice/" + ossPath; String path = "voice/" + ossPath;
if (fileName != null && !fileName.isEmpty() && path.endsWith(fileName)) { if (fileName != null && !fileName.isEmpty() && path.endsWith(fileName)) {
path = path.substring(0, path.length() - fileName.length()); path = path.substring(0, path.length() - fileName.length());
@ -65,6 +77,38 @@ public class FileUploadUtil {
} }
logger.debug("上传文件到OSS, path={}, 大小={} bytes", path, fileData.length); 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 { try {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA); headers.setContentType(MediaType.MULTIPART_FORM_DATA);
@ -99,6 +143,7 @@ public class FileUploadUtil {
Map<?, ?> map = JSON.parseObject(responseBody, Map.class); Map<?, ?> map = JSON.parseObject(responseBody, Map.class);
if (map != null && (map.containsKey("fileUrl") || map.containsKey("url"))) { if (map != null && (map.containsKey("fileUrl") || map.containsKey("url"))) {
String fileUrl = parseFileUrl(responseBody); String fileUrl = parseFileUrl(responseBody);
validateFileUrl(fileUrl, fileName);
logger.info("文件上传完成 (扁平格式), URL: {}", fileUrl); logger.info("文件上传完成 (扁平格式), URL: {}", fileUrl);
return fileUrl; return fileUrl;
} }
@ -111,6 +156,7 @@ public class FileUploadUtil {
ResultEntity result = JSON.parseObject(responseBody, ResultEntity.class); ResultEntity result = JSON.parseObject(responseBody, ResultEntity.class);
if (result != null && result.getCode() != null && result.getCode() == ResultEntity.StatusCode.SUCCESS.getCode()) { if (result != null && result.getCode() != null && result.getCode() == ResultEntity.StatusCode.SUCCESS.getCode()) {
String fileUrl = parseFileUrl(result.getContent()); String fileUrl = parseFileUrl(result.getContent());
validateFileUrl(fileUrl, fileName);
logger.info("文件上传完成 (包装格式), URL: {}", fileUrl); logger.info("文件上传完成 (包装格式), URL: {}", fileUrl);
return fileUrl; return fileUrl;
} else { } 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 * 从响应 content 中解析 fileUrl
* *

Loading…
Cancel
Save