语音管理
This commit is contained in:
@ -1,6 +1,11 @@
|
||||
package com.fuyuanshen.app.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
|
||||
import com.fuyuanshen.app.domain.dto.AppAudioFileDto;
|
||||
import com.fuyuanshen.app.domain.dto.AppFileDto;
|
||||
import com.fuyuanshen.app.domain.dto.TextToSpeechRequest;
|
||||
import com.fuyuanshen.app.domain.vo.AppFileVo;
|
||||
import com.fuyuanshen.app.service.AudioProcessService;
|
||||
import com.fuyuanshen.app.service.VideoProcessService;
|
||||
import com.fuyuanshen.common.core.domain.R;
|
||||
@ -8,6 +13,7 @@ import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit;
|
||||
import com.fuyuanshen.common.web.core.BaseController;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@ -56,6 +62,7 @@ public class AppVideoController extends BaseController {
|
||||
return R.ok(audioProcessService.generateStandardPcmData(text));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 提取文本内容(只支持txt/docx)
|
||||
*/
|
||||
@ -65,4 +72,52 @@ public class AppVideoController extends BaseController {
|
||||
return R.ok("Success",audioProcessService.extract(file));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 上传音频文件进行处理
|
||||
* 支持MP3、WAV、PCM格式
|
||||
*/
|
||||
@PostMapping("/uploadAudioToOss")
|
||||
public R<String> uploadAudioToOss(@RequestParam("file") @ModelAttribute AppAudioFileDto bo) {
|
||||
try {
|
||||
String result = audioProcessService.uploadAudioToOss(bo);
|
||||
return R.ok(result);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return R.fail("文件格式错误: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
|
||||
return R.fail("上传处理失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本转语音
|
||||
* 支持MP3、WAV、PCM格式
|
||||
*/
|
||||
@PostMapping("/ttsToOss")
|
||||
public R<String> textToSpeech(@RequestBody TextToSpeechRequest request) {
|
||||
try {
|
||||
if (request.getDeviceId() == null) {
|
||||
return R.fail("设备ID不能为空");
|
||||
}
|
||||
|
||||
String result = audioProcessService.textToSpeech(
|
||||
request.getDeviceId(),
|
||||
request.getText(),
|
||||
request.getFileSuffix()
|
||||
);
|
||||
return R.ok(result);
|
||||
} catch (Exception e) {
|
||||
return R.fail("文本转语音失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询语音文件列表
|
||||
*/
|
||||
@GetMapping("/queryAudioFileList")
|
||||
public R<List<AppFileVo>> queryAudioFileList(Long deviceId) {
|
||||
return R.ok(audioProcessService.queryAudioFileList(deviceId));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
package com.fuyuanshen.app.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Data
|
||||
public class AppAudioFileDto {
|
||||
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 文件
|
||||
*/
|
||||
private MultipartFile file;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.fuyuanshen.app.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* PCM生成请求实体
|
||||
*/
|
||||
@Data
|
||||
public class PcmGenerationRequest {
|
||||
private String text;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.fuyuanshen.app.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 文本转语音请求实体
|
||||
*/
|
||||
@Data
|
||||
public class TextToSpeechRequest {
|
||||
|
||||
private Long deviceId;
|
||||
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* 文件后缀格式
|
||||
*/
|
||||
private String fileSuffix;
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
public class ApacheHttpTtsClient {
|
||||
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String appKey;
|
||||
|
||||
public ApacheHttpTtsClient(String accessKeyId, String accessKeySecret, String appKey) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
this.appKey = appKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Apache HttpClient调用TTS服务
|
||||
*/
|
||||
public byte[] synthesizeTextToMp3(String text) throws IOException {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
|
||||
try {
|
||||
HttpPost httpPost = new HttpPost("https://nls-gateway.cn-shanghai.aliyuncs.com/v1/tts");
|
||||
|
||||
// 设置请求头
|
||||
httpPost.setHeader("Content-Type", "application/json");
|
||||
httpPost.setHeader("Authorization", "Bearer " + getAccessToken());
|
||||
|
||||
// 设置请求体
|
||||
String jsonPayload = String.format(
|
||||
"{\"appkey\":\"%s\",\"text\":\"%s\",\"voice\":\"zhifeng\",\"format\":\"MP3\",\"sample_rate\":24000,\"volume\":50,\"speech_rate\":0,\"pitch_rate\":0}",
|
||||
appKey,
|
||||
text.replace("\"", "\\\"")
|
||||
);
|
||||
|
||||
StringEntity entity = new StringEntity(jsonPayload, "UTF-8");
|
||||
httpPost.setEntity(entity);
|
||||
|
||||
// 执行请求
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
try {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
if (response.getStatusLine().getStatusCode() == 200) {
|
||||
// 读取响应数据(Base64编码的音频数据)
|
||||
String responseStr = EntityUtils.toString(responseEntity);
|
||||
|
||||
// 如果返回的是Base64编码的数据
|
||||
if (responseStr.startsWith("data:audio/mp3;base64,")) {
|
||||
String base64Data = responseStr.substring("data:audio/mp3;base64,".length());
|
||||
return Base64.getDecoder().decode(base64Data);
|
||||
} else {
|
||||
// 直接返回音频数据
|
||||
return EntityUtils.toByteArray(responseEntity);
|
||||
}
|
||||
} else {
|
||||
throw new IOException("请求失败: " + response.getStatusLine().getStatusCode());
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
} finally {
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问令牌
|
||||
*/
|
||||
private String getAccessToken() {
|
||||
// 实现获取阿里云访问令牌的逻辑
|
||||
return "your-access-token";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import com.alibaba.nls.client.AccessToken;
|
||||
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import static cn.dev33.satoken.SaManager.log;
|
||||
|
||||
public class HttpTtsClient {
|
||||
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String appKey;
|
||||
/**
|
||||
* 阿里云TTS服务基础URL
|
||||
*/
|
||||
private static final String BASE_URL = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";
|
||||
|
||||
public HttpTtsClient(String accessKeyId, String accessKeySecret, String appKey) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
this.appKey = appKey;
|
||||
}
|
||||
private String refreshAccessToken() {
|
||||
try {
|
||||
// 调用阿里云API获取访问令牌
|
||||
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
|
||||
accessToken.apply();
|
||||
String token = accessToken.getToken();
|
||||
log.info("访问令牌刷新成功");
|
||||
|
||||
return token;
|
||||
} catch (Exception e) {
|
||||
log.error("刷新访问令牌失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 使用HTTP POST方式调用阿里云TTS服务生成MP3格式语音
|
||||
*/
|
||||
public byte[] synthesizeTextToMp3(String text) throws IOException {
|
||||
String endpoint = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts";
|
||||
|
||||
// 构建请求体
|
||||
// String requestBody = String.format(
|
||||
// "{\"appkey\":\"%s\",\"text\":\"%s\",\"voice\":\"zhifeng\",\"format\":\"MP3\",\"sample_rate\":24000,\"volume\":50,\"speech_rate\":0,\"pitch_rate\":0}",
|
||||
// appKey,
|
||||
// text.replace("\"", "\\\"")
|
||||
// );
|
||||
|
||||
String token = refreshAccessToken();
|
||||
String requestBody = " {\n" +
|
||||
" \"appkey\":\""+appKey+"\",\n" +
|
||||
" \"text\":\""+text+"\",\n" +
|
||||
" \"token\":\""+token+"\",\n" +
|
||||
" \"format\":\"mp3\"\n" +
|
||||
" }";
|
||||
|
||||
URL url = new URL(endpoint);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
|
||||
// 设置请求方法和头部
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setRequestProperty("Authorization", buildAuthorization());
|
||||
conn.setRequestProperty("Content-Length", String.valueOf(requestBody.getBytes().length));
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// 发送请求体
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
os.write(requestBody.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
// 读取响应
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
// 读取音频数据
|
||||
try (InputStream is = conn.getInputStream()) {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nRead;
|
||||
byte[] data = new byte[1024];
|
||||
while ((nRead = is.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
} else {
|
||||
throw new IOException("HTTP请求失败,状态码: " + responseCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建授权头部
|
||||
*/
|
||||
private String buildAuthorization() {
|
||||
// 实际实现需要根据阿里云API规范构建签名
|
||||
// 这里是简化示例
|
||||
return "Bearer " + generateAccessToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成访问令牌
|
||||
*/
|
||||
private String generateAccessToken() {
|
||||
// 实际实现需要调用阿里云获取token的API
|
||||
return "your-access-token";
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存MP3到文件
|
||||
*/
|
||||
public String saveMp3ToFile(String text, String outputPath) throws IOException {
|
||||
byte[] mp3Data = synthesizeTextToMp3(text);
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
|
||||
fos.write(mp3Data);
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class HttpTtsExample {
|
||||
|
||||
/**
|
||||
* appKey: lbGuq5K5bEH4uxmT
|
||||
* akId: LTAI5t66moCkhNC32TDJ5ReP
|
||||
* akSecret: 2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
String accessKeyId = "LTAI5t66moCkhNC32TDJ5ReP";
|
||||
String accessKeySecret = "2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH";
|
||||
String appKey = "lbGuq5K5bEH4uxmT";
|
||||
|
||||
try {
|
||||
// 使用HTTP方式调用
|
||||
HttpTtsClient httpClient = new HttpTtsClient(accessKeyId, accessKeySecret, appKey);
|
||||
|
||||
String text = "大江东去,浪淘尽,千古风流人物。故垒西边,人道是,三国周郎赤壁。乱石穿空,惊涛拍岸,卷起千堆雪。";
|
||||
String outputPath = "D:\\http_output9.mp3";
|
||||
|
||||
String resultFile = httpClient.saveMp3ToFile(text, outputPath);
|
||||
System.out.println("MP3音频已保存至: " + resultFile);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import okhttp3.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class OkHttpTtsClient {
|
||||
|
||||
private OkHttpClient client = new OkHttpClient();
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String appKey;
|
||||
|
||||
public OkHttpTtsClient(String accessKeyId, String accessKeySecret, String appKey) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
this.appKey = appKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用OkHttp调用TTS服务
|
||||
*/
|
||||
public byte[] synthesizeTextToMp3(String text) throws IOException {
|
||||
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
|
||||
String jsonPayload = String.format(
|
||||
"{\"appkey\":\"%s\",\"text\":\"%s\",\"voice\":\"zhifeng\",\"format\":\"MP3\",\"sample_rate\":24000,\"volume\":50,\"speech_rate\":0,\"pitch_rate\":0}",
|
||||
appKey,
|
||||
text.replace("\"", "\\\"")
|
||||
);
|
||||
|
||||
RequestBody body = RequestBody.create(JSON,jsonPayload);
|
||||
Request request = new Request.Builder()
|
||||
.url("https://nls-gateway.cn-shanghai.aliyuncs.com/v1/tts")
|
||||
.post(body)
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + getAccessToken())
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IOException("HTTP请求失败: " + response.code());
|
||||
}
|
||||
|
||||
ResponseBody responseBody = response.body();
|
||||
if (responseBody != null) {
|
||||
return responseBody.bytes();
|
||||
} else {
|
||||
throw new IOException("响应体为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getAccessToken() {
|
||||
// 实现获取访问令牌的逻辑
|
||||
return "your-access-token";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.fuyuanshen.app.http;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* 获取阿里云TTS服务访问令牌
|
||||
*/
|
||||
public class TokenClient {
|
||||
|
||||
public static String getAccessToken(String accessKeyId, String accessKeySecret) throws IOException {
|
||||
String endpoint = "https://nls-meta.cn-shanghai.aliyuncs.com/v1/token";
|
||||
|
||||
URL url = new URL(endpoint);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// 发送认证信息
|
||||
String params = String.format("grant_type=client_credentials&client_id=%s&client_secret=%s",
|
||||
accessKeyId, accessKeySecret);
|
||||
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
os.write(params.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
try (InputStream is = conn.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
|
||||
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
|
||||
// 解析响应获取token(实际需要使用JSON解析库)
|
||||
return response.toString().replaceAll(".*\"access_token\":\"([^\"]+)\".*", "$1");
|
||||
}
|
||||
} else {
|
||||
throw new IOException("获取令牌失败,状态码: " + responseCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,5 +96,4 @@ public class AppFileService {
|
||||
}
|
||||
return appBusinessFileService.deleteWithValidByIds(List.of(ids), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,16 @@
|
||||
package com.fuyuanshen.app.service;
|
||||
|
||||
import com.fuyuanshen.app.domain.bo.AppBusinessFileBo;
|
||||
import com.fuyuanshen.app.domain.dto.AppAudioFileDto;
|
||||
import com.fuyuanshen.app.domain.vo.AppFileVo;
|
||||
import com.fuyuanshen.app.http.HttpTtsClient;
|
||||
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
||||
import com.fuyuanshen.equipment.utils.AlibabaTTSUtil;
|
||||
import com.fuyuanshen.equipment.utils.AudioProcessUtil;
|
||||
import com.fuyuanshen.equipment.utils.FileHashUtil;
|
||||
import com.fuyuanshen.equipment.utils.Mp3Duration;
|
||||
import com.fuyuanshen.system.domain.vo.SysOssVo;
|
||||
import com.fuyuanshen.system.service.ISysOssService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -17,8 +26,12 @@ import javax.xml.stream.XMLStreamReader;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
@ -39,6 +52,10 @@ public class AudioProcessService {
|
||||
private final AudioProcessUtil audioProcessUtil;
|
||||
private final AlibabaTTSUtil alibabaTTSUtil;
|
||||
|
||||
private final FileHashUtil fileHashUtil;
|
||||
private final ISysOssService ossService;
|
||||
private final IAppBusinessFileService appBusinessFileService;
|
||||
|
||||
/**
|
||||
* 处理上传的音频文件
|
||||
*/
|
||||
@ -305,4 +322,158 @@ public class AudioProcessService {
|
||||
}
|
||||
|
||||
|
||||
public String uploadAudioToOss(AppAudioFileDto bo) {
|
||||
MultipartFile file = bo.getFile();
|
||||
// 校验文件格式和大小
|
||||
validateAudioFileForRestful(file);
|
||||
// 上传文件
|
||||
// SysOssVo upload = sysOssService.upload(file);
|
||||
|
||||
try {
|
||||
String fileHash = fileHashUtil.hash(file);
|
||||
SysOssVo upload = ossService.updateHash(file, fileHash);
|
||||
// 强制将HTTP替换为HTTPS
|
||||
if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
|
||||
upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
|
||||
}
|
||||
|
||||
AppBusinessFileBo appBusinessFileBo = new AppBusinessFileBo();
|
||||
appBusinessFileBo.setFileId(upload.getOssId());
|
||||
appBusinessFileBo.setBusinessId(bo.getDeviceId());
|
||||
appBusinessFileBo.setFileType(3L);
|
||||
appBusinessFileBo.setCreateBy(AppLoginHelper.getUserId());
|
||||
|
||||
appBusinessFileService.insertByBo(appBusinessFileBo);
|
||||
if (upload != null) {
|
||||
return upload.getUrl();
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("上传音频文件失败", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验音频文件格式
|
||||
*/
|
||||
private void validateAudioFileForRestful(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new IllegalArgumentException("上传文件不能为空");
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null) {
|
||||
throw new IllegalArgumentException("文件名不能为空");
|
||||
}
|
||||
|
||||
// 检查文件扩展名
|
||||
if (!isSupportedFormat(originalFilename)) {
|
||||
throw new IllegalArgumentException("只允许上传MP3、WAV、PCM格式的音频文件");
|
||||
}
|
||||
|
||||
// 检查文件大小
|
||||
if (file.getSize() > MAX_AUDIO_SIZE) {
|
||||
throw new IllegalArgumentException("音频大小不能超过5MB");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件是否为支持的音频格式
|
||||
*/
|
||||
private boolean isSupportedFormat(String filename) {
|
||||
if (filename == null || filename.lastIndexOf('.') == -1) {
|
||||
return false;
|
||||
}
|
||||
String ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
|
||||
return SUPPORTED_FORMATS.contains(ext);
|
||||
}
|
||||
|
||||
public String textToSpeech(Long deviceId,String text, String fileSuffix) {
|
||||
//支持PCM/WAV/MP3格式
|
||||
if (fileSuffix == null || fileSuffix.isEmpty()) {
|
||||
fileSuffix = "mp3";
|
||||
}
|
||||
fileSuffix = fileSuffix.toLowerCase();
|
||||
List<String> SUPPORTED_FORMATS = Arrays.asList(
|
||||
"wav", "mp3", "pcm"
|
||||
);
|
||||
boolean contains = SUPPORTED_FORMATS.contains(fileSuffix);
|
||||
if (!contains) {
|
||||
throw new IllegalArgumentException("不支持的音频格式");
|
||||
}
|
||||
String accessKeyId = "LTAI5t66moCkhNC32TDJ5ReP";
|
||||
String accessKeySecret = "2F3sdoBJ08bYvJcuDgSkLnJwGXsvYH";
|
||||
String appKey = "lbGuq5K5bEH4uxmT";
|
||||
String savedPath = null;
|
||||
try {
|
||||
// 使用HTTP方式调用
|
||||
HttpTtsClient httpClient = new HttpTtsClient(accessKeyId, accessKeySecret, appKey);
|
||||
//
|
||||
byte[] mp3Data = httpClient.synthesizeTextToMp3(text);
|
||||
// byte[] mp3Data = alibabaTTSUtil.synthesizeTextToMp3(text);
|
||||
|
||||
SysOssVo upload = ossService.upload(mp3Data, generateRandomFileName(fileSuffix));
|
||||
|
||||
// 强制将HTTP替换为HTTPS
|
||||
if (upload.getUrl() != null && upload.getUrl().startsWith("http://")) {
|
||||
upload.setUrl(upload.getUrl().replaceFirst("^http://", "https://"));
|
||||
}
|
||||
|
||||
|
||||
AppBusinessFileBo appBusinessFileBo = new AppBusinessFileBo();
|
||||
appBusinessFileBo.setFileId(upload.getOssId());
|
||||
appBusinessFileBo.setBusinessId(deviceId);
|
||||
appBusinessFileBo.setFileType(3L);
|
||||
appBusinessFileBo.setCreateBy(AppLoginHelper.getUserId());
|
||||
savedPath = saveByteArrayToFile(mp3Data, generateRandomFileName(fileSuffix));
|
||||
if (savedPath != null) {
|
||||
log.info("MP3文件已保存: {}", savedPath);
|
||||
Integer mp3Duration = Mp3Duration.getMp3Duration(savedPath);
|
||||
log.info("MP3文件时长: {} 秒", mp3Duration);
|
||||
appBusinessFileBo.setDuration(mp3Duration);
|
||||
}
|
||||
appBusinessFileService.insertByBo(appBusinessFileBo);
|
||||
if (upload != null) {
|
||||
return upload.getUrl();
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("上传音频文件失败", e);
|
||||
} finally {
|
||||
log.info("删除临时文件: {}", savedPath);
|
||||
if(savedPath != null){
|
||||
deleteTempFile(new File(savedPath));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final Random random = new Random();
|
||||
private static final DateTimeFormatter formatter =
|
||||
DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
/**
|
||||
* 生成纯随机文件名(无原文件名依赖)
|
||||
*/
|
||||
public static String generateRandomFileName(String extension) {
|
||||
String timestamp = LocalDateTime.now().format(formatter);
|
||||
int randomNum = random.nextInt(10000);
|
||||
String uuidPart = UUID.randomUUID().toString().substring(0, 8);
|
||||
|
||||
if (!extension.startsWith(".")) {
|
||||
extension = "." + extension;
|
||||
}
|
||||
|
||||
return timestamp + "_" + String.format("%04d", randomNum) + "_" + uuidPart + extension;
|
||||
}
|
||||
|
||||
public List<AppFileVo> queryAudioFileList(Long deviceId) {
|
||||
if(deviceId == null){
|
||||
return null;
|
||||
}
|
||||
AppBusinessFileBo bo = new AppBusinessFileBo();
|
||||
bo.setBusinessId(deviceId);
|
||||
bo.setFileType(3L);
|
||||
List<AppFileVo> appFileVos = appBusinessFileService.queryAppFileList(bo);
|
||||
return appFileVos;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user