终于折腾好了(使用FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字,我的是Vue.js项目),对一个前端仔来说,听到要兼容IE,都是不情愿的,但是需求来了,那就折腾起来吧。(主要这需求砍不了,菜刀没带)。但是经过这几天折腾,记录一下,不足之处,还望指教~

首先呢,明确下我们的目标,支持IE11浏览器录音后转文字。我们用逆向思维思考下,音频转文字。我们目前用的是科大讯飞的SDK。前端的话,把我们拿到的音频数据,包装好,调用一下后端给的接口,然后后端调讯飞SDK,转化为文字,发回来给我们。所以我们最主要是拿到音频数据,即录音。那我们难点是IE11上录音,因为IE 不支持 navigator.mediaDevices.getUserMedia这样的API。这里用JS调用flash去实现IE上录音。(应该还可以调用ActiveX,没尝试)。

这里在github找到两个可以运行起来的demo。传送门:recorder.js,FlashWavRecorder

这两个提供的example 都可以正常跑起来。recorder.js音质比较好,第二个杂音大点。但recorder.js不知道怎么取到录音后的音频数据,照理说,上传都可以实现了,拿到音频数据,理应可以。或许是我的能力问题,还请指点。FlashWavRecorder 有getBase64的方法,所以就选用FlashWavRecorder 了。

调用别人的SDK,就要符合别人的游戏规则。如下摘自科大讯飞接入规则

1. 接口说明

  1. 授权认证,调用接口需要将 Appid,CurTime, Param 和 CheckSum 信息放在 HTTP 请求头中;
  2. 接口统一为 UTF-8 编码;
  3. 接口支持 http 和 https;
  4. 请求方式为POST。

2. 授权认证

在调用所有业务接口时,都需要在 Http Request Header 中配置以下参数用于授权认证:

参数 格式 说明 必须
X-Appid string 讯飞开放平台注册申请应用的应用ID(appid)
X-CurTime string 当前UTC时间戳,从1970年1月1日0点0 分0 秒开始到现在的秒数
X-Param string 相关参数JSON串经Base64编码后的字符串,见各接口详细说明
X-CheckSum string 令牌,计算方法:MD5(apiKey + curTime + param),三个值拼接的字符串,进行MD5哈希计算(32位小写),其中apiKey由讯飞提供,调用方管理。

讯飞demo中有这个字段 文档没写

body string dataUrl  

注:

  • apiKey:接口密钥,由讯飞开放平台提供,调用方注意保管,如泄露,可联系讯飞技术人员重置;
  • checkSum 有效期:出于安全性考虑,每个 checkSum 的有效期为 5 分钟(用 curTime 计算),同时 curTime 要与标准时间同步,否则,时间相差太大,服务端会直接认为 curTime 无效;
  • BASE64 编码采用 MIME 格式,字符包括大小写字母各26个,加上10个数字,和加号 + ,斜杠 / ,一共64个字符。

checkSum 生成示例:

String apiKey="abcd1234";
String curTime="1502607694";
String param="eyAiYXVmIjogImF1ZGlvL0wxNjtyYXR...";
String checkSum=MD5(apiKey+curTime+param);
Copy

3. 白名单

在调用所有业务接口时,授权认证通过后,服务端会检查调用方ip是否在讯飞开放平台配置的ip白名单中,对于没有配置到白名单中的IP发来的请求,服务端会拒绝服务。 注:

  • IP白名单可在控制台应用管理卡片上编辑,五分钟左右生效;
  • IP白名单最多可设置5个,更多的需求可通过工单联系技术人员;
  • 如果服务器返回结果如下所示,则表示由于未配置IP白名单,服务端拒绝服务:
{"code":"10105","desc":"illegal access|illegal client_ip","data":"","sid":"xxxxxx"
}

我们请求后端接口类似这样:

that.$http.postObj('restful/voice/recognition', {'body': sendurl,'X-Appid': that.appid,'X-CurTime': time,'X-Param': xParam,'X-CheckSum': xChecksum}).then((res) => {//成功识别语音后,在这里拿到文字 eg:res.data.dataconsole.log(res)}}).catch((err) => {console.log(err)});
//basic.js  $(function () {
//这里的$(function () {})  原生写法是function DOMContentLoaded(){};
//document.addEventListener('DOMContentLoaded', DOMContentLoaded, false);var RECORDER_APP_ID = "recorderApp";var $level = $('.level .progress');var appWidth = 24;var appHeight = 24;var flashvars = {'upload_image': '../images/upload.png'};var params = {};var attributes = {'id': RECORDER_APP_ID, 'name': RECORDER_APP_ID};swfobject.embedSWF("../recorder.swf", "flashcontent", appWidth, appHeight, "11.0.0", "", flashvars, params, attributes); //注意这句,要确保swfobject.js 已经加载好了 要准备个容器不然报错 <div id="flashcontent" />//这里使用  document.getElementById(RECORDER_APP_ID).style.top='-3333px' // 因为把swf对象设置为display:none 或者visibility:hidden 都是使用不了 window.fwr_event_handler = function fwr_event_handler() {//flash插件相关状态反馈var name, $controls;switch (arguments[0]) {case "ready":FWRecorder.uploadFormId = "#uploadForm";FWRecorder.uploadFieldName = "upload_file[filename]";FWRecorder.connect(RECORDER_APP_ID, 0);//如果老是报 recoder is null 这里没有调进来FWRecorder.recorderOriginalWidth = appWidth;FWRecorder.recorderOriginalHeight = appHeight;break;case "permission_panel_closed":FWRecorder.recorderOriginalWidth = 0;FWRecorder.recorderOriginalHeight = 0;document.getElementById(RECORDER_APP_ID).style.top='-3333px'//隐藏白白一块FWRecorder.defaultSize();break;case "microphone_user_request":FWRecorder.showPermissionWindow();//这里请求后 会弹出 是否 允许 使用麦克风的请求break;....这里省略局部代码case "microphone_level"://这里用于显示声波。如果成功接入麦克风,录音的时候会一直调用$level.css({height: arguments[1] * 100 + '%'});break;....这里省略局部代码};.......这里省略局部代码
});

以上主要是理解swfobject.embedSWF的用法。这里有详细的传送门: swfobject.js使用

//recoder.js  (function(global) {var Recorder;var RECORDED_AUDIO_TYPE = "audio/wav";Recorder = {recorder: null,recorderOriginalWidth: 0,recorderOriginalHeight: 0,uploadFormId: null,uploadFieldName: null,isReady: false,connect: function(name, attempts) {//连接麦克风 主要是init recorderif(navigator.appName.indexOf("Microsoft") != -1) {Recorder.recorder = window[name];} else {Recorder.recorder = document[name];//swfObject对象 ,这里的 name='recorderApp'}if(attempts >= 40) {return;}// flash app needs time to load and initializeif(Recorder.recorder && Recorder.recorder.init) {Recorder.recorderOriginalWidth = Recorder.recorder.width;Recorder.recorderOriginalHeight = Recorder.recorder.height;if(Recorder.uploadFormId && $) {var frm = $(Recorder.uploadFormId); Recorder.recorder.init(frm.attr('action').toString(), Recorder.uploadFieldName, frm.serializeArray());}return;}setTimeout(function() {Recorder.connect(name, attempts+1);}, 100);},playBack: function(name) {//播放// TODO: Rename to `playback`Recorder.recorder.playBack(name);},stopRecording: function() {//停止录音Recorder.recorder.stopRecording();},resize: function(width, height) {//设置 麦克风权限请求框大小Recorder.recorder.width = width + "px";Recorder.recorder.height = height + "px";},defaultSize: function() {Recorder.resize(Recorder.recorderOriginalWidth, Recorder.recorderOriginalHeight);},show: function() {Recorder.recorder.show();},hide: function() {Recorder.recorder.hide();},getBase64: function(name) {//来获取base64数据 如果需要转回来 window.atob("base64")var data = Recorder.recorder.getBase64(name);return 'data:' + RECORDED_AUDIO_TYPE + ';base64,' + data;},getBlob: function(name) {//这里转化为blod 后面讯飞转文字的时候要用到。当然可以自己转var base64Data = Recorder.getBase64(name).split(',')[1];return base64toBlob(base64Data, RECORDED_AUDIO_TYPE);},showPermissionWindow: function(options) {Recorder.resize(240, 160);// need to wait until app is resized before displaying permissions screenvar permissionCommand = function() {if (options && options.permanent) {Recorder.recorder.permitPermanently();} else {Recorder.recorder.permit();}};setTimeout(permissionCommand, 1);},};function base64toBlob(b64Data, contentType, sliceSize) {contentType = contentType || '';sliceSize = sliceSize || 512;var byteCharacters = atob(b64Data);var byteArrays = [];for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {var slice = byteCharacters.slice(offset, offset + sliceSize);var byteNumbers = new Array(slice.length);for (var i = 0; i < slice.length; i++) {byteNumbers[i] = slice.charCodeAt(i);}var byteArray = new Uint8Array(byteNumbers);byteArrays.push(byteArray);}return new Blob(byteArrays, {type: contentType});}global.FWRecorder = Recorder;})(this);

然后写几个button 接入就好了。      大家可以动手试一试。

注意:如果是在vue.js中当成模块的时候,ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";

不能使用arguments.callee;不能使用arguments.caller;禁止this指向全局对象;不能使用fn.caller和fn.arguments获取函数调用的堆栈 等等   所以引入swfobject.js 的时候 要把 arguments.callee 替换掉。 传送门:严格模式下arguments.callee代替思路

例如有用arguments.callee的函数里,function (){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);把匿名函数 改为具名函数 ?    swfobject.js 里面好像有7处需要改 function fn(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(fn,0);

基于FlashWavRecorder实现IE11浏览器录音后用科大讯飞转文字相关推荐

  1. 注册表设置IE11浏览器默认属性

    注册表设置IE11浏览器默认属性 工作需要使用IE11浏览器,我们经常会对IE11浏览器做重置后的操作,但是IE11重置后会一直提示"设置向导"或者询问是否要设置为默认浏览器选项, ...

  2. Win10系统 IE11浏览器调用F12开发人员工具,打开后底部显示空白

    Win10系统 IE11浏览器点击F12开发人员工具,打开后底部显示空白. 经过尝试发现该问题与安全选项里的一个策略有关系. 在安全选项里,找到"用户帐户控制:以管理员批准模式运行所有管理员 ...

  3. 难道google也这么小气? 安装Chrome后基于ie内核的浏览器ie6、greenbrowser、myie2均无法打开Google 软件精选?

    今天打开浏览器,默认google的主页里多了一个Google软件精选,好奇就进去看看,却发现打开后网页里什么内容都没有,还以为google还没做好呢,就没在意.谁知道打开Chrome却可以进去,怪啊. ...

  4. Win7 系统更新后IE11浏览器打不开怎么办?

    方法一 1.首先,按下Win+R组合键,在出现的运行命令中输入"regedit"后回车,进入注册表编辑器. 2.在注册表编辑器界面,展开HKEY_CURRENT_USER\Soft ...

  5. IE11浏览器中的My97日历控件刷新后无法打开问题解决办法

    IE11浏览器中的My97日历控件刷新后无法打开问题解决办法:(谷歌浏览器下正常.IE11失效) 解决办法: 1:找到WdatePicker.js 2:将$crossFrame:true 改为$cro ...

  6. 基于Linux的嵌入式浏览器的实现

    随着信息技术与网络技术的快速发展,以及人们日常生活对网络信息的需求的不断增加.掌上电脑.上网本.手机.导航仪器.智能电视.智能家电等对嵌入式系统提出了更多更高的要求.在嵌入式系统中利用嵌入式浏览器获取 ...

  7. mac 下基于firebreath 开发多浏览器支持的浏览器插件

    mac 下基于firebreath 开发多浏览器支持的浏览器插件 首先要区分什么是浏览器扩展和浏览器插件;插件可以像本地程序一样做的更多 一. 关于 firebreath http://www.fir ...

  8. ie11浏览器可以下载java吗_如何卸载IE11? 如何安装低版本的IE浏览器?

    现在软件的更新愈来愈频繁,但是有的时候我们自己的电脑配置太低,IE更新之后,有些软件不能运行,这时候便需要卸载我们电脑上新版本的IE,重装低版本的.但如何卸载IE呢?下面我就来给大家详细的介绍IE的卸 ...

  9. 基于RecyclerView的图片浏览器

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.作业要求 二.具体实现 1.布局代码 2.程序代码 总结 前言 基于RecyclerView的图片浏览器 一.作业 ...

最新文章

  1. 如何获得Oracle系统性能统计? Oracle大型数据库系统在AIXUNIX上的实战详解 集中讨...
  2. java cqlfilter_Geoserver系列:将CQL转换OGC的xml过滤
  3. mysql date_format 按不同时间单位进行分组统计
  4. SDH光端机和PDH光端机有什么区别?
  5. VS2010报错 error:LINK1123:转换到COF期间失败,文件无限或损坏
  6. 登陆mysql服务器命令_Mysql服务器登陆,启动,停止等基本操作命令介绍(Linux/Centos环境)...
  7. zabbix3.2自动发现
  8. 多台电脑集群运算_Linux服务器集群概念辨识
  9. Atitit 返回http500返回码,以及自定义返回提示 目录 1. 可以看到从php发送http响应代码的三种方法: 1 1.1. Header(HTTP/1.1 404 Not Found
  10. 51单片机交通灯(十字路口演示)
  11. DBeaver mysql驱动连接问题
  12. 动手学深度学习-windows下环境配置
  13. Mac怎么读写NTFS格式?Mac读写NTFS格式硬盘教程
  14. 放弃高薪的程序员,体制内或许才是最好的归宿
  15. 项目管理(课程总结1)Week1 澳大利亚维多利亚大学VIT1203 Introduction to Project Management
  16. css设定元素左对齐,左对齐标签 - 右对齐选择元素(CSS)
  17. laravel faker 数据填充
  18. 计蒜客-A1530 Abiyoyo
  19. segment的理解
  20. [week2]每周总结与工作计划

热门文章

  1. linux 下动手实现bash -lR 命令
  2. 【SQL注入技巧拓展】————13、我的WafBypass之道(SQL注入篇)
  3. [iOS]高德地图SDK开发--准备篇
  4. IE9修改收藏夹位置
  5. 房屋装修设计更显档次需要从几个方面入手
  6. Unity3D基础知识学习笔记
  7. 使用math.sin时报错only size-1 arrays can be converted to Python scalar
  8. 可视化接口管理平台 YApi,让你轻松搞定 API 的管理问题
  9. 手机为什么手机里的计算机没在桌面,为什么桌面图标不见了
  10. android_54a3,碉堡了,连神画Y1投影仪都可以跑android大型游戏N.0.V.A3