到ai智能工牌的录音后上传到minio并保存数据库的逻辑
This commit is contained in:
@@ -197,6 +197,8 @@ public class PasswordUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -181,6 +181,8 @@ public class ServiceManager {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -68,10 +68,19 @@ public class SoundRecordingUploadController {
|
|||||||
log.info("收到录音上传请求 - deviceNo: {}, fileName: {}, chunkIndex: {}, startTime: {}, endTime: {}",
|
log.info("收到录音上传请求 - deviceNo: {}, fileName: {}, chunkIndex: {}, startTime: {}, endTime: {}",
|
||||||
deviceNo, fileName, chunkIndex, startTime, endTime);
|
deviceNo, fileName, chunkIndex, startTime, endTime);
|
||||||
|
|
||||||
|
// 记录业务层处理开始时间
|
||||||
|
long startTimeMillis = System.currentTimeMillis();
|
||||||
|
|
||||||
// 调用服务层处理业务逻辑
|
// 调用服务层处理业务逻辑
|
||||||
SoundRecordingUploadServiceImpl.ResponseData serviceResponse = soundRecordingUploadService.uploadRecording(
|
SoundRecordingUploadServiceImpl.ResponseData serviceResponse = soundRecordingUploadService.uploadRecording(
|
||||||
deviceNo, deviceType, soundRecording, fileName, chunkIndex, startTime, endTime, usrNo);
|
deviceNo, deviceType, soundRecording, fileName, chunkIndex, startTime, endTime, usrNo);
|
||||||
|
|
||||||
|
// 记录业务层处理结束时间并计算耗时
|
||||||
|
long endTimeMillis = System.currentTimeMillis();
|
||||||
|
long elapsedTime = endTimeMillis - startTimeMillis;
|
||||||
|
double elapsedTimeSeconds = elapsedTime / 1000.0;
|
||||||
|
log.info("业务层处理完成 - deviceNo: {}, 耗时: {} 秒", deviceNo, String.format("%.2f", elapsedTimeSeconds));
|
||||||
|
|
||||||
// 转换服务层返回的结果为响应对象
|
// 转换服务层返回的结果为响应对象
|
||||||
SoundRecordingUploadResponse response = new SoundRecordingUploadResponse();
|
SoundRecordingUploadResponse response = new SoundRecordingUploadResponse();
|
||||||
response.setCode(serviceResponse.code);
|
response.setCode(serviceResponse.code);
|
||||||
|
|||||||
@@ -419,6 +419,8 @@ public class MenuController {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -389,6 +389,8 @@ public class RoleController {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -395,6 +395,8 @@ public class UserRoleController {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ public class DifyWorkflowResponseDto {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -160,6 +160,8 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
69
src/main/java/com/rj/service/impl/CosyVoiceServiceImpl.java
Normal file
69
src/main/java/com/rj/service/impl/CosyVoiceServiceImpl.java
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package com.rj.service.impl;
|
||||||
|
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CosyVoiceServiceImpl {
|
||||||
|
|
||||||
|
// Xinference集群地址和模型ID(替换为你的实际信息)
|
||||||
|
private static final String XINFERENCE_URL = "http://192.168.1.39:9997/v1/audio/speech";
|
||||||
|
private static final String MODEL_UID = "cosyvoice2-0.5b"; // 模型部署时的UID或名称
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate;
|
||||||
|
|
||||||
|
// 注入RestTemplate
|
||||||
|
public CosyVoiceServiceImpl(RestTemplate restTemplate) {
|
||||||
|
this.restTemplate = restTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用CosyVoice2生成语音
|
||||||
|
* @param text 待转换的文本
|
||||||
|
* @param outputPath 音频保存路径(如"output.wav")
|
||||||
|
* @return 是否成功
|
||||||
|
*/
|
||||||
|
public boolean generateSpeech(String text, String outputPath) {
|
||||||
|
try {
|
||||||
|
// 1. 构造请求体
|
||||||
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
|
requestBody.put("model", MODEL_UID); // 模型ID
|
||||||
|
requestBody.put("input", text); // 输入文本
|
||||||
|
// 可选参数:调整语音风格、语速等(根据模型支持的参数添加)
|
||||||
|
requestBody.put("voice", "default"); // 语音风格(默认值)
|
||||||
|
requestBody.put("speed", 1.0); // 语速(1.0为正常)
|
||||||
|
|
||||||
|
// 2. 设置请求头(JSON格式)
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
|
// 3. 发送POST请求
|
||||||
|
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
|
||||||
|
ResponseEntity<byte[]> response = restTemplate.postForEntity(
|
||||||
|
XINFERENCE_URL,
|
||||||
|
request,
|
||||||
|
byte[].class
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. 处理响应(保存音频文件)
|
||||||
|
if (response.getStatusCode() == HttpStatus.OK) {
|
||||||
|
byte[] audioData = response.getBody();
|
||||||
|
if (audioData != null) {
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(new File(outputPath))) {
|
||||||
|
fos.write(audioData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
package com.rj.service.impl;
|
package com.rj.service.impl;
|
||||||
|
|
||||||
|
import com.rj.entity.AudioManagement;
|
||||||
|
import com.rj.service.IAudioManagementService;
|
||||||
import com.rj.service.ISoundRecordingUploadService;
|
import com.rj.service.ISoundRecordingUploadService;
|
||||||
|
import com.rj.service.MinIOService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,6 +27,12 @@ import java.util.regex.Pattern;
|
|||||||
@Service
|
@Service
|
||||||
public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadService {
|
public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MinIOService minIOService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAudioManagementService audioManagementService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分片索引正则表达式
|
* 分片索引正则表达式
|
||||||
* A开头:启动/运行段,如A001, A005
|
* A开头:启动/运行段,如A001, A005
|
||||||
@@ -50,13 +65,60 @@ public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadSer
|
|||||||
log.info("分片信息解析 - prefix: {}, chunkNumber: {}, isLastChunk: {}, totalChunks: {}",
|
log.info("分片信息解析 - prefix: {}, chunkNumber: {}, isLastChunk: {}, totalChunks: {}",
|
||||||
chunkInfo.prefix, chunkInfo.chunkNumber, chunkInfo.isLastChunk, chunkInfo.totalChunks);
|
chunkInfo.prefix, chunkInfo.chunkNumber, chunkInfo.isLastChunk, chunkInfo.totalChunks);
|
||||||
|
|
||||||
// TODO: 这里可以添加文件保存逻辑
|
// 上传文件到 MinIO
|
||||||
// 1. 保存文件到指定目录
|
String minioUrl;
|
||||||
// 2. 如果是分片上传,需要管理分片状态
|
try {
|
||||||
// 3. 如果是最后一片,需要合并分片或标记完成
|
minioUrl = minIOService.uploadFile(soundRecording);
|
||||||
|
log.info("文件上传到MinIO成功 - deviceNo: {}, fileName: {}, minioUrl: {}", deviceNo, fileName, minioUrl);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("文件上传到MinIO失败 - deviceNo: {}, fileName: {}, error: {}", deviceNo, fileName, e.getMessage(), e);
|
||||||
|
return createErrorResponse(500, 1, "文件上传失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
log.info("录音文件上传成功 - deviceNo: {}, fileName: {}, fileSize: {} bytes",
|
// 转换时间格式(yyMMddHHmmss -> LocalDateTime)
|
||||||
deviceNo, fileName, soundRecording.getSize());
|
LocalDateTime recordingStartTime = parseTimeString(startTime);
|
||||||
|
LocalDateTime recordingEndTime = parseTimeString(endTime);
|
||||||
|
|
||||||
|
// 计算录音时长(分钟)
|
||||||
|
BigDecimal duration = calculateDuration(recordingStartTime, recordingEndTime);
|
||||||
|
|
||||||
|
// 获取文件扩展名
|
||||||
|
String fileExtension = getFileExtension(fileName);
|
||||||
|
|
||||||
|
// 创建录音管理记录
|
||||||
|
AudioManagement audioManagement = new AudioManagement();
|
||||||
|
audioManagement.setId(UUID.randomUUID().toString());
|
||||||
|
audioManagement.setRecordingName(fileName);
|
||||||
|
audioManagement.setRecordingTime(recordingStartTime);
|
||||||
|
audioManagement.setDuration(duration);
|
||||||
|
audioManagement.setAudioFileUrl(minioUrl);
|
||||||
|
audioManagement.setAudioFileSize(soundRecording.getSize());
|
||||||
|
audioManagement.setAudioFileOriginalName(fileName);
|
||||||
|
audioManagement.setAudioFileExtension(fileExtension);
|
||||||
|
audioManagement.setUploadTime(LocalDateTime.now());
|
||||||
|
audioManagement.setUploadStatus("已上传");
|
||||||
|
audioManagement.setSyncStatus("未同步");
|
||||||
|
audioManagement.setIsMerged(chunkInfo.isLastChunk && chunkInfo.totalChunks > 1);
|
||||||
|
|
||||||
|
// 如果有自定义编号,可以存储到备注中
|
||||||
|
if (StringUtils.hasText(usrNo)) {
|
||||||
|
audioManagement.setRemarks("设备编号: " + usrNo + ", 设备类型: " + (deviceType != null ? deviceType : "未知"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置创建和更新时间
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
audioManagement.setCreateTime(now);
|
||||||
|
audioManagement.setUpdateTime(now);
|
||||||
|
|
||||||
|
// 保存到数据库
|
||||||
|
boolean saved = audioManagementService.save(audioManagement);
|
||||||
|
if (!saved) {
|
||||||
|
log.error("保存录音记录到数据库失败 - deviceNo: {}, fileName: {}", deviceNo, fileName);
|
||||||
|
return createErrorResponse(500, 1, "保存录音记录失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("录音文件上传成功并保存到数据库 - deviceNo: {}, fileName: {}, fileSize: {} bytes, audioId: {}",
|
||||||
|
deviceNo, fileName, soundRecording.getSize(), audioManagement.getId());
|
||||||
|
|
||||||
// 返回成功响应
|
// 返回成功响应
|
||||||
return createSuccessResponse();
|
return createSuccessResponse();
|
||||||
@@ -152,6 +214,62 @@ public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadSer
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析时间字符串(yyMMddHHmmss -> LocalDateTime)
|
||||||
|
*/
|
||||||
|
private LocalDateTime parseTimeString(String timeStr) {
|
||||||
|
try {
|
||||||
|
// 将 yy 转换为 yyyy(假设 yy < 50 表示 20xx,否则表示 19xx)
|
||||||
|
int year = Integer.parseInt(timeStr.substring(0, 2));
|
||||||
|
if (year < 50) {
|
||||||
|
year += 2000;
|
||||||
|
} else {
|
||||||
|
year += 1900;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造完整的日期时间字符串
|
||||||
|
String fullTimeStr = String.format("%04d%s", year, timeStr.substring(2));
|
||||||
|
DateTimeFormatter fullFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||||
|
return LocalDateTime.parse(fullTimeStr, fullFormatter);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("时间格式解析失败 - timeStr: {}, error: {}", timeStr, e.getMessage());
|
||||||
|
return LocalDateTime.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算录音时长(分钟)
|
||||||
|
*/
|
||||||
|
private BigDecimal calculateDuration(LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
|
if (startTime == null || endTime == null) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
long seconds = java.time.Duration.between(startTime, endTime).getSeconds();
|
||||||
|
if (seconds < 0) {
|
||||||
|
log.warn("录音结束时间早于开始时间,返回0");
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为分钟,保留2位小数
|
||||||
|
double minutes = seconds / 60.0;
|
||||||
|
return BigDecimal.valueOf(minutes).setScale(2, java.math.RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件扩展名
|
||||||
|
*/
|
||||||
|
private String getFileExtension(String filename) {
|
||||||
|
if (!StringUtils.hasText(filename)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int lastDotIndex = filename.lastIndexOf('.');
|
||||||
|
if (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) {
|
||||||
|
return filename.substring(lastDotIndex + 1).toLowerCase();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分片信息内部类
|
* 分片信息内部类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,8 @@ spring:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -317,6 +317,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
96
src/test/java/com/rj/audio/SenseVoiceAsrTest.java
Normal file
96
src/test/java/com/rj/audio/SenseVoiceAsrTest.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package com.rj.audio;
|
||||||
|
|
||||||
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class SenseVoiceAsrTest {
|
||||||
|
// Xinference集群地址(替换为你的实际地址)
|
||||||
|
private static final String XINFERENCE_ASR_URL = "http://101.35.52.237:19997/v1/audio/transcriptions";
|
||||||
|
// 已部署的SenseVoiceSmall模型名称(必须与部署时一致)
|
||||||
|
private static final String MODEL_NAME = "SenseVoiceSmall";
|
||||||
|
// 本地测试音频文件路径(需是模型支持的格式,如.wav)
|
||||||
|
private static final String AUDIO_FILE_PATH = "D:\\bCard\\code\\Langchain4j-rj\\output_longyingling.mp3"; // 替换为你的音频文件路径
|
||||||
|
// private static final String AUDIO_FILE_PATH = "http://101.35.52.237:19005/car/20251027_141728_de21b339944c4e43867afc903a511905.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20251102%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251102T012236Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=9ceb9ea5361da2dbf830e5c0ad68c1a0cb73573cf039148225f53457d83e1b47"; // 替换为你的音频文件路径
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 1. 初始化RestTemplate
|
||||||
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2. 读取音频文件为字节数组
|
||||||
|
byte[] audioData = Files.readAllBytes(Paths.get(AUDIO_FILE_PATH));
|
||||||
|
|
||||||
|
// 3. 构造multipart/form-data请求体
|
||||||
|
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||||
|
// 添加模型参数
|
||||||
|
body.add("model", MODEL_NAME);
|
||||||
|
// 添加音频文件(key固定为"file",文件名可自定义)
|
||||||
|
body.add("file", new ByteArrayResource(audioData) {
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return "output_longyingling.mp3"; // 必须指定文件名(含扩展名)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 可选参数:指定语言(如中文"zh-CN",根据模型支持添加)
|
||||||
|
body.add("language", "zh-CN");
|
||||||
|
|
||||||
|
// 4. 设置请求头(multipart/form-data)
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||||
|
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(body, headers);
|
||||||
|
|
||||||
|
// 5. 发送POST请求,获取转录结果
|
||||||
|
ResponseEntity<String> response = restTemplate.postForEntity(
|
||||||
|
XINFERENCE_ASR_URL,
|
||||||
|
request,
|
||||||
|
String.class
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* {
|
||||||
|
* "text": "转录文本",
|
||||||
|
* "audio_duration": 13.114, # 音频时长(秒)
|
||||||
|
* "transcription_time": 0.337, # 转录耗时(秒)
|
||||||
|
* "segment_count": 1, # 分段数
|
||||||
|
* "char_count": 50 # 字数(新增)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
System.out.println("音频识别结果:" + response.toString());
|
||||||
|
// 6. 处理响应(解析JSON获取文本)
|
||||||
|
if (response.getStatusCode() == HttpStatus.OK) {
|
||||||
|
String responseBody = response.getBody();
|
||||||
|
// 解析JSON:Xinference返回格式通常为 {"text": "识别结果文本"}
|
||||||
|
// 实际项目中建议使用Jackson/Gson解析,这里简化处理
|
||||||
|
String transcription = extractTextFromJson(responseBody);
|
||||||
|
System.out.println("音频识别结果:" + transcription);
|
||||||
|
} else {
|
||||||
|
System.out.println("请求失败,状态码:" + response.getStatusCode());
|
||||||
|
System.out.println("错误信息:" + response.getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("读取音频文件失败:" + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("API调用失败:" + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简易JSON解析(提取"text"字段)
|
||||||
|
private static String extractTextFromJson(String json) {
|
||||||
|
if (json == null || !json.contains("\"text\":")) {
|
||||||
|
return "解析失败,响应:" + json;
|
||||||
|
}
|
||||||
|
int start = json.indexOf("\"text\":") + 7;
|
||||||
|
int end = json.indexOf("}", start);
|
||||||
|
return json.substring(start, end).replace("\"", "").trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/test/java/com/rj/audio/SenseVoiceByXinferenceTest.java
Normal file
59
src/test/java/com/rj/audio/SenseVoiceByXinferenceTest.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package com.rj.audio;
|
||||||
|
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SenseVoiceByXinferenceTest {
|
||||||
|
// Xinference 集群地址(替换为你的实际地址)
|
||||||
|
private static final String XINFERENCE_URL = "http://192.168.1.39:9997/v1/audio/speech";
|
||||||
|
// 已部署的模型名称
|
||||||
|
private static final String MODEL_NAME = "SenseVoiceSmall";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 1. 初始化 RestTemplate
|
||||||
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
// 2. 构造请求体:指定模型、输入文本
|
||||||
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
|
requestBody.put("model", MODEL_NAME);
|
||||||
|
requestBody.put("input", "这是一段测试SenseVoiceSmall模型的语音合成文本。");
|
||||||
|
// 可选参数:调整语音风格、语速等(根据模型支持的参数添加)
|
||||||
|
requestBody.put("voice", "default"); // 语音风格
|
||||||
|
requestBody.put("speed", 1.0); // 语速(1.0为正常)
|
||||||
|
|
||||||
|
// 3. 设置请求头为 JSON 格式
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 4. 发送 POST 请求,获取音频二进制数据
|
||||||
|
ResponseEntity<byte[]> response = restTemplate.postForEntity(
|
||||||
|
XINFERENCE_URL,
|
||||||
|
request,
|
||||||
|
byte[].class
|
||||||
|
);
|
||||||
|
|
||||||
|
// 5. 处理响应:保存音频到本地
|
||||||
|
if (response.getStatusCode() == HttpStatus.OK) {
|
||||||
|
byte[] audioData = response.getBody();
|
||||||
|
if (audioData != null) {
|
||||||
|
String outputPath = "sense_voice_test.wav";
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(new File(outputPath))) {
|
||||||
|
fos.write(audioData);
|
||||||
|
System.out.println("语音合成成功,已保存到:" + outputPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("请求失败,状态码:" + response.getStatusCode());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -255,6 +255,8 @@ public class FaceDetectImageCountTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -196,6 +196,8 @@ public class TtsRequestLogShortUrlTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ public class VideoSynthesisTempUrlTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,8 @@ public class VideoSynthesisVideoNameTest {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user