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

Java后端代码

maven 仓库:

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

maven 仓库官方 tts 使用案例:

@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

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

业务 service

@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);
}
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;
}
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[] 数组

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 注入:

@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;
}
public static void setValue(String key, byte[] value, Long expireTime, TimeUnit timeUnit) {
    redisByteTemplate.opsForValue().set(key, value, expireTime, timeUnit);
}
public static byte[] getByte(String key) {
    return redisByteTemplate.opsForValue().get(key);
}

前端代码

<!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 即可

启动调试

在这里插入图片描述
嗯,完美运行,下班收工

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/559472.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【论文源码实战】轻量化MobileSAM,分割一切大模型出现,模型缩小60倍,速度提高40倍

前言 MobileSAM模型是在2023年发布的&#xff0c;其对之前的SAM分割一切大模型进行了轻量化的优化处理&#xff0c;模型整体体积缩小了60倍&#xff0c;运行速度提高40倍&#xff0c;但分割效果却依旧很好。 MobileSAM在使用方法上沿用了SAM模型的接口&#xff0c;因此可以与…

超越GPT-4V,苹果多模态大模型上新,神经形态计算加速MLLM(一)

4月8日&#xff0c;苹果发布了其最新的多模态大语言模型&#xff08;MLLM &#xff09;——Ferret-UI&#xff0c;能够更有效地理解和与屏幕信息进行交互&#xff0c;在所有基本UI任务上都超过了GPT-4V&#xff01; 苹果开发的多模态模型Ferret-UI增强了对屏幕的理解和交互&am…

【YOLOv8改进[Backbone]】使用MobileNetV3助力YOLOv8网络结构轻量化并助力涨点

目录 一 MobileNetV3 1 面向块搜索的平台感知NAS和NetAdapt 2 反向残差和线性瓶颈 二 使用MobileNetV3助力YOLOv8 1 整体修改 ① 添加MobileNetV3.py文件 ② 修改ultralytics/nn/tasks.py文件 ③ 修改ultralytics/utils/torch_utils.py文件 2 配置文件 3 训练 其他 …

内置管线升级到SBP,如何复用之前打包的AssetBundle

1&#xff09;内置管线升级到SBP&#xff0c;如何复用之前打包的AssetBundle 2&#xff09;安卓真机&#xff0c;在Unity 2021.3.31版本下Buffer数据异常 3&#xff09;URP里CullResults.CreateSharedRendererScene下面的消耗 4&#xff09;移动端是否支持曲面细分着色 这是第3…

C#基础|Debug程序调试学习和技巧总结

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 在程序的开发过程中&#xff0c;可能绝大部分时间是用来调试程序&#xff0c; 当完成了某个功能的编程&#xff0c;都需要调试一下程序&#xff0c;看编程是否存在问题。 01 为什么需要程序调试 无论是电气工程师还…

Zed,有望打败 VS Code 吗?

大家好&#xff0c;我是楷鹏。 先说结论&#xff0c;不行。 Zed&#xff0c;又一款新起的文本代码编辑器 &#x1f449; https://zed.dev 今年一月二十四号正式开源&#xff0c;短短不到三个月&#xff0c;GitHub 上已经冲上 3 万 star 正如 Zed 的口号所说「Code at the spe…

win11家庭中文版安装docker遇到Hyper-V启用失败,如何解决??

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

统一所有 LLM API:支持预算与速率限制 | 开源日报 No.229

BerriAI/litellm Stars: 6.7k License: NOASSERTION litellm 是一个使用 OpenAI 格式调用所有 LLM API 的工具。它支持 Bedrock、Azure、OpenAI、Cohere、Anthropic 等 100 多种 LLMs&#xff0c;提供企业级代理服务器和稳定版本 v1.30.2。 主要功能和优势包括&#xff1a; 将…

Jenkins的安装和部署

文章目录 概述Jenkins部署项目的流程jenkins的安装启动创建容器进入容器浏览器访问8085端口 Jenkins创建项目创建example项目 概述 Jenkins&#xff1a;是一个开源的、提供友好操作界面的持续集成&#xff08;CLI&#xff09;工具&#xff0c;主要用于持续、自动构建的一些定时…

nVisual在线网络规划设计软件

●01● nVisual在线网络规划设计软件 在信息化快速发展的今天&#xff0c;网络基础设施的建设与优化变得尤为关键。为了满足现代通信行业对高效、精准的网络规划需求&#xff0c;nVisual在线网络规划设计软件应运而生&#xff0c;它通过集成先进的GIS技术和网络规划工具&#…

如何快速学习盲打键盘的指法

学习盲打键盘的指法需要一定的时间和练习&#xff0c;但是以下几个方法可以帮助你加快学习的速度&#xff1a; 掌握正确的手位&#xff1a;了解标准的键盘布局以及手指应该放置的位置是学习盲打的第一步。在QWERTY键盘上&#xff0c;你的左手应该放在ASDF键上&#xff0c;右手应…

基于开源项目改造,我制作了15个酷炫的数据大屏(附 Python 源码)

数据可视化大屏在许多领域都有广泛的应用&#xff0c;其带来了好处也是显而易见的&#xff1a; 直观展示数据&#xff1a; 大屏幕数据可视化能够将庞大的数据集以图形化的方式展示出来&#xff0c;使人们能够更容易地理解和分析数据。这种可视化形式使信息更加直观&#xff0c;…

SSM项目前后端分离详细说明

1.后端 1.1打包 说明&#xff1a;使用idea打开项目&#xff0c;然后进行打包。 1.2tomcat 说明&#xff1a;把后端打成war包后放入tomcat启动。 1.3启动tomcat 说明&#xff1a; 找到tomcat中bin目录中的startup.bat文件&#xff0c;进行启动。如果启动失败&#xff0c;可以…

Stream 流常见基本操作

文章目录 概述一、Stream 流的常见生成方式二、Stream 流中间操作方法1、常用中间操作方法2、使用示例13、使用示例24、使用示例35、使用示例46、使用示例57、Stream 流使用注意事项 三、Stream 流终结操作方法1、常用终结方法2、使用示例13、使用示例24、使用示例35、Stream 基…

界面控件DevExpress Blazor UI v23.2 - 浅谈增强的可访问性

DevExpress Blazor UI组件库提供了一套全面的原生Blazor组件(包括DataGrid、Pivot Grid、 调度程序、图表、数据编辑器和报表)&#xff0c;使用C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验&#xff01; 获取DevExpress v23.2正式版下载(Q技术交流&#xff1…

【Linux开发 第三篇】vmtools安装,快照

虚拟机克隆 方式一 直接拷贝一份安装好的虚拟机文件&#xff0c;再用VM打开文件即可 方式二 使用vmware的克隆操作&#xff08;克隆时要先关闭Linux系统&#xff09; 虚拟机快照 如果你在使用虚拟机的时候&#xff0c;担心现在的操作&#xff0c;想回到操作之前的状态&a…

开放式耳机哪个牌子好?热门开放式耳机合集,买前必看!

随着人们对运动健康的重视&#xff0c;越来越多的运动爱好者开始关注如何在运动中享受音乐。开放式蓝牙耳机凭借其独特的设计&#xff0c;成为了户外运动的理想选择。它不仅让你在运动时能够清晰听到周围环境的声音&#xff0c;保持警觉&#xff0c;还能让你在需要时与他人轻松…

08 JavaScript学习:数据类型

JavaScript 数据类型 值类型(基本类型)&#xff1a;字符串&#xff08;String&#xff09;、数字(Number)、布尔(Boolean)、空&#xff08;Null&#xff09;、未定义&#xff08;Undefined&#xff09;、Symbol。 引用数据类型&#xff08;对象类型&#xff09;&#xff1a;对…

插入排序与希尔排序

文章目录 插入排序配图详解核心思想核心代码 源代码运行结果 希尔排序实现逻辑源代码运行结果 插入排序 插入排序在少量数据中是一个高效的算法&#xff0c;你可以想象在打牌的时候&#xff0c;左手是已经整理好的牌&#xff0c;右手是正在抓取的牌。 配图详解 对一组数据 5&…

手机号码空号过滤API:有效验证和过滤无效电话号码

随着移动通信技术的发展&#xff0c;手机号码成为人们日常生活和工作中不可或缺的一部分。然而&#xff0c;随着时间的推移&#xff0c;一些手机号码可能会变成空号&#xff0c;这给企业在进行电话营销和数据分析时带来了一定的困扰。为了解决这个问题&#xff0c;挖数据平台提…
最新文章