openAI tts Java文本转语音完整前后端代码 html

Java后端代码

maven 仓库:

xml 复制代码
<!--openAI 请求工具-->
<dependency>
    <groupId>com.unfbx</groupId>
    <artifactId>chatgpt-java</artifactId>
    <version>1.1.5</version>
</dependency>

maven 仓库官方 tts 使用案例:

java 复制代码
@SneakyThrows
@Test
public void textToSpeed() {
    TextToSpeech textToSpeech = TextToSpeech.builder()
            .model(TextToSpeech.Model.TTS_1.getName())
            .input("OpenAI官方Api的Java SDK,可以快速接入项目使用。目前支持OpenAI官方全部接口,同时支持Tokens计算。官方github地址:https://github.com/Grt1228/chatgpt-java。欢迎star。")
            .voice(TtsVoice.NOVA.getName())
            .responseFormat(TtsFormat.MP3.getName())
            .build();
    java.io.File file = new java.io.File("G:\\test.mp3");
    ResponseBody responseBody = client.textToSpeech(textToSpeech);
    InputStream inputStream = responseBody.byteStream();
    //创建文件
    if (!file.exists()) {
        if (!file.getParentFile().exists())
            file.getParentFile().mkdir();
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("createNewFile IOException");
        }
    }

    OutputStream os = null;
    try {
        os = new BufferedOutputStream(new FileOutputStream(file));
        byte data[] = new byte[8192];
        int len;
        while ((len = inputStream.read(data, 0, 8192)) != -1) {
            os.write(data, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (os != null) {
                os.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

控制器 controller

java 复制代码
@ApiOperation("文本转语音")
@GetMapping("/textToVoice")
public ResponseEntity<InputStreamResource> textToVoice(String text, HttpServletResponse response) {
    response.setContentType("application/octet-stream");
    return translationHistoryService.textToVoice(text);
}

业务 service

java 复制代码
@Override
public ResponseEntity<InputStreamResource> textToVoice(String text) {
    String audioDownloadUrl = ObjectUtils.filterObjectNull(this.lambdaQuery()
            .select(TranslationHistory::getAudioDownloadUrl)
            .eq(TranslationHistory::getOriginalText, text)
            .last(SqlConstants.LIMIT_1).one(), TranslationHistory.class)
            .getAudioDownloadUrl();
    InputStream inputStream = chatGptService.textToSpeed(text,audioDownloadUrl);
    InputStreamResource resource = new InputStreamResource(inputStream);
    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(resource);
}
java 复制代码
public InputStream textToSpeed(String text,String audioDownloadUrl) {
    // 1、查询缓存
    String redisKey = RedisKeyUtils.getTextToSpeed(text);
    byte[] audioData = RedisUtils.getByte(redisKey);
    if (audioData != null) {
       log.info("从缓存中返回音频流数据");
       return new ByteArrayInputStream(audioData);
    }
    // 2、调用 api 响应
    // 将生成的音频数据读取为字节数组
    InputStream inputStream = null;
    try {
       log.info("从 API 中返回音频流数据");
       inputStream = textToSpeedIs(text);
       audioData = inputStreamToByteArray(inputStream);
       // 将音频缓存到 Redis 中
       RedisUtils.setValue(redisKey, audioData,7L, TimeUnit.DAYS);
       byte[] finalAudioData = audioData;
       // 开辟线程存入 redis
       poolSend.send(()->{
          // 标记首次
          String cacheKey = RedisKeyUtils.getFirstTextToSpeed(text);
          RedisUtils.setValue(cacheKey, finalAudioData,2L, TimeUnit.DAYS);
       });
    } catch (IOException e) {
       log.error("openAI音频调用异常:",e  );
       throw new RuntimeException(e);
    } finally {
       // 平常的关闭流代码太难看了,写了工具类简洁多了,自己封装一个
       CloseableUtils.close(inputStream);
    }
    // 将文本和对应的音频数据缓存到 Redis 中
    RedisUtils.setValue(redisKey, audioData, 7L, TimeUnit.DAYS);
    inputStream = new ByteArrayInputStream(audioData);
    return inputStream;
}
java 复制代码
private InputStream textToSpeedIs(String text) throws IOException {
    TextToSpeech textToSpeech = TextToSpeech.builder()
          .model(TextToSpeech.Model.TTS_1.getName())
          .input(text)
          .voice(TtsVoice.NOVA.getName())
          .responseFormat(TtsFormat.MP3.getName())
          .build();
    // (重点,这里的方法细节就不展示了,看官方案例就知道,在哪个基础上复制粘贴封装一下方法即可)
    ResponseBody responseBody = chatGptStreamRequest.textToSpeech(textToSpeech,openaiKeyService.getApiKeyStrList());
    InputStream inputStream = responseBody.byteStream();
    return inputStream;
}

将 inputStream 流转换成 byte[] 数组

Java 复制代码
public static byte[] inputStreamToByteArray(InputStream inputStream) throws IOException {
    ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
    int bufferSize = 1024;
    byte[] buffer = new byte[bufferSize];

    int len;
    while ((len = inputStream.read(buffer)) != -1) {
       byteBuffer.write(buffer, 0, len);
    }

    byte[] bytes = byteBuffer.toByteArray();
    byteBuffer.close();
    inputStream.close(); // 关闭流以释放资源

    return bytes;
}

后端代码就是这样,哦对了,还有 redisTemplate 的配置也分享一下,因为要将音频 byte[] 存入缓存,所以单独给 byte[] 类型配置 redisTemplate 注入:

java 复制代码
@Bean
public RedisTemplate<String, byte[]> byteRedisTemplate(RedisConnectionFactory connectionFactory) {
    RedisTemplate<String, byte[]> template = new RedisTemplate<>();
    template.setConnectionFactory(connectionFactory);
    template.setKeySerializer(new StringRedisSerializer());
    // 使用 ByteArrayRedisSerializer 来处理字节数据
    template.setValueSerializer(RedisSerializer.byteArray());
    template.afterPropertiesSet();
    return template;
}
Java 复制代码
public static void setValue(String key, byte[] value, Long expireTime, TimeUnit timeUnit) {
    redisByteTemplate.opsForValue().set(key, value, expireTime, timeUnit);
}
Java 复制代码
public static byte[] getByte(String key) {
    return redisByteTemplate.opsForValue().get(key);
}

前端代码

Java 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>音频流测试</title>
</head>
<body>
    <h2>音频流播放测试</h2>
    <!-- 音频播放组件,初始时不显示 -->
    <audio id="audioPlayer" controls style="display: none;"></audio>

    <button id="playAudio">播放音频</button>

    <script>
        document.getElementById('playAudio').addEventListener('click', function() {
            // 音频流接口的 URL
            var audioUrl = 'http://localhost:8822/client/translation/textToVoice?text=今天很开心1';

            // 使用 Fetch API 请求音频流
            fetch(audioUrl, {
                method: 'GET',
                headers: {
                    // 添加请求头
                    'token': 'v0Dbf55iGiH8uSfxwrlkvlt12qb57cnj'
                }
            }).then(function(response) {
                // 检查响应是否成功
                if (response.ok) {
                    return response.blob();
                }
                throw new Error('网络响应错误');
            }).then(function(blob) {
                // 将 Blob 转换为 URL 并设置给 <audio> 元素
                var url = URL.createObjectURL(blob);
                var audioPlayer = document.getElementById('audioPlayer');
                audioPlayer.src = url;
                audioPlayer.style.display = 'block';
                audioPlayer.play();
            }).catch(function(error) {
                console.error('请求音频流失败:', error);
            });
        });
    </script>
</body>
</html>

过程中出现的后端异常

User
No converter for [class cn.hutool.core.io.resource.InputStreamResource] with preset Content-Type 'application/octet-stream'

将 class cn.hutool.core.io.resource.InputStreamResource 切换成 org.springframework.core.io.InputStreamResource 即可

启动调试

嗯,完美运行,下班收工

相关推荐
lizi888881 小时前
单组件的编写
java
java_heartLake1 小时前
设计模式之代理模式
java·设计模式·代理模式
change95131 小时前
PHP纯离线搭建(php 8.1.7)
开发语言·php
福鸦1 小时前
详解c++:new和delete
开发语言·c++
qq_172805592 小时前
Go Testify学习与使用
开发语言·golang·go
魏 无羡2 小时前
pgsql 分组查询方法
java·服务器·数据库
兩尛2 小时前
java--面向对象编程(中级部分)
java·开发语言
卡戎-caryon2 小时前
【Linux】09.Linux 下的调试器——gdb/cgdb
linux·运维·服务器·开发语言·笔记
Xxxx. .Xxxx2 小时前
C语言程序设计实验与习题指导 (第4版 )课后题-第二章+第三章
java·c语言·开发语言
姜西西_2 小时前
[Spring]Spring MVC 请求和响应及用到的注解
java·spring·mvc