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;// 开辟线程存入 redispoolSend.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() {// 音频流接口的 URLvar 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.mzph.cn/news/826111.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅析RED和EN 18031

文章目录 前言欧盟的法律文件什么是REDRED的发展EU 2022/30法规EU 2023/2444RED与EN 18031的关系 前言 提示&#xff1a;本文大致表述了欧盟的一些立法常识&#xff0c;RED的由来与发展&#xff0c;以及它跟EN 18031的关系 因为工作原因&#xff0c;最近稍微研究了一下欧盟即将…

微波炉定时器开关

微波炉火力调节开关及定时器开关内部结构 参考链接&#xff1a; 微波炉火力调节开关及定时器开关判断好坏小经验-百度经验 (baidu.com)https://jingyan.baidu.com/article/5d6edee2d175c399eadeecfd.html微波炉拆解图示&#xff0c;微波炉结构原理&#xff0c;轻松玩转微波炉维…

AI大模型探索之路-应用篇14:认识国产开源大模型GLM

目录 前言 一、国产主流大模型概览 1. 国内主流大模型清单 2. 主流大模型综合指数 3. 大语言模型评测榜单 二、GLM大模型介绍 三、GLM大模型发展历程 四、GLM家族之基座模型GLM-130B 五、GLM家族之ChatGLM3 六、GLM家族之WebGLM 七、GLM家族之CogVLM 1. CogVLM 2. …

春招冲刺百题计划|栈

Java基础复习 Java数组的声明与初始化Java ArrayListJava HashMapJava String 类Java LinkedListJava Deque继承LinkedListJava Set 第一题&#xff1a;有效的括号 很简单的题&#xff0c;从大一做到现在&#xff0c;就是复习一下语法。 class Solution {public boolean i…

系统架构最佳实践 -- 新能源汽车产业架构设计

随着环保意识的增强和能源结构的转型&#xff0c;新能源汽车产业正迅速崛起成为汽车行业的新宠。构建一个完善的新能源汽车产业架构对于推动产业发展、提升竞争力至关重要。本文将从设计原则、关键技术、产业生态等方面&#xff0c;探讨如何设计与实现新能源汽车产业架构。 新能…

LabVIEW专栏六、LabVIEW项目

一、梗概 项目&#xff1a;后缀是.lvproj&#xff0c;在实际开发的过程中&#xff0c;一般是要用LabVIEW中的项目来管理代码&#xff0c;也就是说相关的VI或者外部文件&#xff0c;都要放在项目中来管理。 在LabVIEW项目中&#xff0c;是一个互相依赖的整体&#xff0c;可以包…

TongRds docker 镜像做成与迁移(by liuhui)

TongRds docker 镜像做成与迁移 一&#xff0c;使用 docker commit 命令制作 TongRds docker 镜 像 1.1 拉取基础镜像 centos 并运行该镜像 拉取镜像&#xff1a;docker pull ubuntu 镜像列表&#xff1a;docker images 运行镜像&#xff1a;docker run -itd --name myubuntu…

每日一题(L2-011):玩转二叉树--建树+层序遍历

与L2-006近乎相同&#xff0c;先建树&#xff0c;然后遍历 #include<bits/stdc.h> using namespace std; int in[35]; int pre[35]; typedef struct Tree{int num;Tree* left;Tree* right; }T;T * build(int in1,int in2,int pre1,int pre2){T * tnew T;t->numpre[pr…

西宁市初中生地会考报名照片尺寸要求及手机自拍方法

西宁市初中生地会考即将到来&#xff0c;对于参加考试的同学们来说&#xff0c;准备一张符合规格的报名照片是整个报名流程中不可或缺的一环。一张规范的证件照不仅展示了学生的精神面貌&#xff0c;同时也是顺利报名的重要条件之一。本文将详细介绍西宁市初中生地会考报名所需…

LLM长度外推理论与实践

定义&#xff1a; 长度外推&#xff0c;即免训练长度外推&#xff0c;就是不需要用长序列数据进行额外的训练&#xff0c;只用短序列语料对模型进行训练&#xff0c;就可以得到一个能够处理和预测长序列的模型&#xff0c;即“Train Short, Test Long”。 判断方法&#xff1…

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

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

Java对象克隆-浅拷贝与深拷贝

目录 1、对象的克隆 1.1 对象的浅拷贝 1.2 对象深拷贝 1、对象的克隆 1.1 对象的浅拷贝 在实际编程过程中&#xff0c;我们常常要遇到这种情况&#xff1a;有一个对象A&#xff0c;在某一时刻A中已经包含了一些有效值&#xff0c;此时可能会需要一个和A完全相同新对象B&am…

论婚恋相亲交友软件的市场前景和开发方案H5小程序APP源码

随着移动互联网的快速发展和社交需求的日益增长&#xff0c;婚恋相亲交友软件小程序成为了越来越多单身人士的选择。本文将从市场前景、使用人群、盈利模式以及竞品分析等多个角度&#xff0c;综合论述这一领域的现状与发展趋势。 一、市场前景 在快节奏的现代生活中&#xf…

得物sign参数逆向分析与Python算法还原

文章目录 1. 写在前面2. 接口分析3. 断点分析4. Python算法还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚…

Postman调用OpenApi接口

首先你需要科学上网。。。。。 请求方式&#xff1a;post 请求地址&#xff1a;https://api.openai.com/v1/chat/completions 请求头&#xff1a; Authorization : Bearer key Content-Type : application/json Body : { "messages": [{ "role": &quo…

【论文精读】Bag of Tricks for Image Classification with Convolutional Neural Networks

这篇文章比较早了&#xff0c;2018年的 摘要 最近在图像分类研究方面取得的大部分进展可以归功于训练程序的改进&#xff0c;如数据增强和优化方法的改变。然而&#xff0c;在文献中&#xff0c;大多数改进要么作为实现细节简要提到&#xff0c;要么只在源代码中可见。在本文中…

CV method:最新Backbone---TransNeXt

文章目录 前言一、提出问题二、模型结构1. Pixel-focused Attention&#xff08;像素聚焦注意力机制&#xff09;2. Aggregating Diverse Attentions in a Single Mixer Query embedding3. ConvGLU4. 整体模型架构 三、方法论1. PFA设计原理2. Aggregating Diverse Attention原…

前端从零到一搭建脚手架并发布到npm

这里写自定义目录标题 一、为什么需要脚手架&#xff1f;二、前置-第三方工具的使用1. 创建demo并运行-4步新建文件夹 zyfcli&#xff0c;并初始化npm init -y配置入口文件 2.commander-命令行指令3. chalk-命令行美化工具4. inquirer-命令行交互工具5. figlet-艺术字6. ora-lo…

【算法】合并两个有序链表

本题来源---《合并两个有序链表》 题目描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] /*** Definition for singl…

C++ - STL详解—vector类

一. vector的概念 向量&#xff08;Vector&#xff09;是一个封装了动态大小数组的顺序容器&#xff08;Sequence Container&#xff09;。跟任意其它类型容器一样&#xff0c;它能够存放各种类型的对象。可以简单的认为&#xff0c;向量是一个能够存放任意类型的动态数组。 …