微信原文地址:智能接口 / 开放接口 / 获取语音识别结果 

        由上面官方文档可知,微信平台中提供了两个接口:第一个接口是语音上传接口,第二个接口是获取识别结果接口。

        所以我们可以通过前端录音,然后将录音传至后端,由后端再去先后请求以上两个接口,以此达到语音转文字(STT)的目的。

重要!!!==》要先申请下来公众号或者服务号。因为后面要用appId和secret密钥来请求。

先获取到微信的token,然后携带这个token去请求微信接口。

第一部分:前端生成录音文件:

        前端生成录音部分,可采用第三方的插件:Recorder: html5 js 录音 mp3 wav ogg webm amr g711a g711u 格式,支持pc和Android、iOS部分浏览器、Hybrid App(提供Android iOS App源码)、微信,提供ASR语音识别转文字 H5版语音通话聊天示例 DTMF编码解码

使用方式我参考了这位老哥的文章:vue3实现H5网页录音并上传(mp3、wav)兼容Android、iOS和PC端使用 Recorder插件 可以在HT - 掘金

不得不说,此插件非常方便。

第二部分,后端调用微信的两个接口:

        因为微信语音上传接口规定了请求方式,所以不再赘述。

 1、创建form-data式的请求(本人技术有限,可能写的有些潜在问题,比如流的关闭应该放在finally块中):

此段代码参考了这位老哥的文章:Java以form-data(表单)的形式调用第三方接口_java formdata-CSDN博客

    //前缀
    public static String PREFIX = "--";
    //换行符
    public static String ROW = "\r\n";
    //产生一个边界
    static String BOUNDARY = UUID.randomUUID().toString().replaceAll("-" , "");
    /**
     * 发送form-data表单数据
     * @param url
     * @param paramMap
     * @param inputStream
     * @return
     */
    public static String sendPostWithFormData(String url,Map<String,String> paramMap,DataInputStream dataInputStream,String fileName) {

        StringBuffer bufferUrl = new StringBuffer();
        bufferUrl.append(url);
        int mapSize = paramMap.size();
        if (!paramMap.isEmpty()){
            bufferUrl.append("?");
            for (String mapKey : paramMap.keySet()) {
                bufferUrl.append(mapKey+"=");
                bufferUrl.append(paramMap.get(mapKey));
                mapSize--;
                if (mapSize>0){
                    bufferUrl.append("&");
                }
            }
        }

        DataOutputStream outputStream = null;
        try {
            URL myUrl = new URL(bufferUrl.toString());
            HttpURLConnection connection = (HttpURLConnection) myUrl.openConnection();

            connection.setRequestMethod("POST");
            connection.setRequestProperty("accept","*/*");
            connection.setRequestProperty("connection","Keep-Alive");
            connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.setRequestProperty("Content-Type","multipart/form-data;boundary="+BOUNDARY);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setDefaultUseCaches(false);
            //连接超时
            connection.setConnectTimeout(10000);
            //读取超时
            connection.setReadTimeout(20000);


            outputStream = new DataOutputStream(connection.getOutputStream());

            StringBuffer bufferData = new StringBuffer();
            bufferData.append(PREFIX).append(BOUNDARY).append(ROW);
            bufferData.append("Content-Disposition: form-data; name=\"");
            bufferData.append("uploadFiles").append("\"; ");
            bufferData.append("filename=\"").append(fileName).append("\"").append(ROW);
            bufferData.append("Content-Type: application/octet-stream; charset=UTF-8");



            byte[] bates = new byte[1024*1024];
            int length = 0;
            while ((length=dataInputStream.read(bates))!=-1){
                outputStream.write(bates,0,length);
            }
            dataInputStream.close();
            outputStream.write(ROW.getBytes(StandardCharsets.UTF_8));

            //写入边界结束符
            outputStream.write((PREFIX + BOUNDARY + PREFIX + ROW).getBytes(StandardCharsets.UTF_8));
            outputStream.flush();//可以理解为发送请求

            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            return bufferedReader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
        return null;
    }

2、普通的Http Post请求:

 /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url
     *            发送请求的 URL
     * @param paramMap
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPostTwo(String url, Map<String, String> paramMap) {
        OutputStreamWriter out = null;
        BufferedReader in = null;
        String result = "";
        StringBuffer param=new StringBuffer();
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            HttpURLConnection  conn = (HttpURLConnection) realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Accept-Charset", "UTF-8");
            conn.setRequestProperty("contentType", "UTF-8");
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new OutputStreamWriter(conn.getOutputStream(),"UTF-8");
            // 发送请求参数
            Iterator it = paramMap.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry entry = (Map.Entry) it.next();
                param.append(entry.getKey().toString());
                param.append("=");
                param.append(entry.getValue().toString());
                param.append("&");
            }
            out.write(param.toString().substring(0, param.toString().length()-1));
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(),"UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result+=line;
            }
            result=new String(result.getBytes(),"utf-8");
        } catch (Exception e) {
//            System.out.println("发送 POST 请求出现异常!"+e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }

3、微信两个请求接口的工具类

@Slf4j
public class AudioToTextUtil {

    private static final String WX_AUDIO_TO_TEXT = "https://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext";

    //上传语音文件
    public static JSONObject wxSTT(MultipartFile multipartFile, String accessToken){
        JSONObject jsonObject = new JSONObject();
        if (multipartFile.isEmpty()){
            jsonObject.put("code",-1);
            return jsonObject;
        }
        String id = UUID.randomUUID().toString().replaceAll("-", "");
        try{
            DataInputStream dataInputStream = new DataInputStream(multipartFile.getInputStream());
            Map<String,String> map = new HashMap<>();
            map.put("access_token",accessToken);
            map.put("format","mp3");
            map.put("voice_id",id);
            map.put("lang","zh_CN");
            String responseData = HttpRequest.sendPostWithFormData(WX_AUDIO_TO_TEXT, map, dataInputStream, "PMSAUDIO");
            jsonObject.put("data",responseData);
            jsonObject.put("id",id);
            log.error("语音信息upload==》{}",responseData);
            return jsonObject;
        } catch (IOException ioException) {
            log.error("语音识别失败,id==>{},原因==>{}",id,ioException.getMessage());
            throw new BaseApiException("语音识别失败,id==>{}",id);
        }
    }


    //获取识别结果

    public static String wxSTTResult(String accessToken,String id){

        Map<String,String> map = new HashMap<>();
        map.put("access_token",accessToken);
        map.put("voice_id",id);
        map.put("lang","zh_CN");

        String postWx = HttpRequest.sendPostTwo("https://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext", map);
        log.error("语音识别信息success==》{}",postWx);
        return postWx;
    }
}

4、controller层的调用部分

@RestController
@RequestMapping("/management/sTTCenter")
public class STTController extends AbstractBaseController {

    @Value("${weChat.token.appid}")
    private String appid;

    @Value("${weChat.token.secret}")
    private String appsecret;

    @PostMapping("/STTParse")
    public MonoDataBodyResponse<Object> audioToText(@RequestBody MultipartFile multipartFile) throws InterruptedException,WxErrorException {
        AccessToken accessToken = WxApi.getAccessToken(appid, appsecret);
        String accessTokenAccessToken = accessToken.getAccessToken();
        JSONObject jsonObject = AudioToTextUtil.wxSTT(multipartFile, accessTokenAccessToken);
        if(!Objects.isNull(jsonObject) && Objects.isNull(jsonObject.get("code"))){
            Thread.sleep(500L);
            String sttResult = AudioToTextUtil.wxSTTResult(accessTokenAccessToken, String.valueOf(jsonObject.get("id")));
            JSONObject result = JSONObject.parseObject(sttResult);
            return new MonoDataBodyResponse<Object>().addBody(result);
        }
        return new MonoDataBodyResponse<Object>().addBody(jsonObject);
    }
}

到此,没有意外的话应该是可以实现上面说的语音转文字了。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐