uniapp中上传音频只能在app或小程序当中实现,如何使用网页完成语音的录制和上传则成为了困扰前端童鞋的重点。
本文着重解决:
js-audio-recorder报 error:浏览器不支持getUserMedia ! 的问题。
js-audio-recorder报 NotFoundError : Requested device not found 的问题
js-audio-recorder的使用、demo以及文件上传的问题。

本篇文章主要讲如何使用uniapp(vue)在网页中(web模式下)录制声音并上传
js-audio-recorder插件支持在微信公众号(网页)、UC浏览器、Chrome及其内核的浏览器内运行。

文章前半部分先讲操作和demo,后半部分再讲原理和插件来源。

demo编写——前期准备:

  1. 安装插件
yarn add js-audio-recorder

如果需要转mp3加一句:

yarn add lamejs
  1. 建立vue文件

由于代码是从网上上复制下来的,个人做了一些修改,所以会看到一些注释。
此代码上传按钮会直接上传文件到后端,请修改为自己的后端地址


<template><div class="home" style="margin:1vw;"><Button type="success" @click="getPermission()" style="margin:1vw;">获取麦克风权限</Button><br/><Button type="info" @click="startRecorder()"  style="margin:1vw;">开始录音</Button><Button type="info" @click="resumeRecorder()" style="margin:1vw;">继续录音</Button><Button type="info" @click="pauseRecorder()" style="margin:1vw;">暂停录音</Button><Button type="info" @click="stopRecorder()" style="margin:1vw;">结束录音</Button><br/><Button type="success" @click="playRecorder()" style="margin:1vw;">录音播放</Button><Button type="success" @click="pausePlayRecorder()" style="margin:1vw;">暂停录音播放</Button><Button type="success" @click="resumePlayRecorder()" style="margin:1vw;">恢复录音播放</Button><Button type="success" @click="stopPlayRecorder()" style="margin:1vw;">停止录音播放</Button><br/><Button type="info" @click="getRecorder()" style="margin:1vw;">获取录音信息</Button><Button type="info" @click="downPCM()" style="margin:1vw;">下载PCM</Button><Button type="info" @click="downWAV()" style="margin:1vw;">下载WAV</Button><Button type="info" @click="getMp3Data()" style="margin:1vw;">下载MP3</Button><br/><Button type="error" @click="destroyRecorder()" style="margin:1vw;">销毁录音</Button><br/><Button type="error" @click="uploadWav()" style="margin:1vw;">上传录音</Button><br/><div style="width:100%;height:200px;border:1px solid red;"><canvas id="canvas"></canvas><span style="padding: 0 10%;"></span><canvas id="playChart"></canvas></div></div>
</template><script>import Recorder from 'js-audio-recorder'const lamejs = require('lamejs')const recorder = new Recorder({sampleBits: 16,                 // 采样位数,支持 8 或 16,默认是16sampleRate: 48000,              // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000numChannels: 1,                 // 声道,支持 1 或 2, 默认是1// compiling: false,(0.x版本中生效,1.x增加中)  // 是否边录边转换,默认是false})// 绑定事件-打印的是当前录音数据recorder.onprogress = function(params) {// console.log('--------------START---------------')// console.log('录音时长(秒)', params.duration);// console.log('录音大小(字节)', params.fileSize);// console.log('录音音量百分比(%)', params.vol);// console.log('当前录音的总数据([DataView, DataView...])', params.data);// console.log('--------------END---------------')}export default {name: 'home',data () {return {//波浪图-录音drawRecordId:null,oCanvas : null,ctx : null,//波浪图-播放drawPlayId:null,pCanvas : null,pCtx : null,}},mounted(){this.startCanvas();},methods: {/*** 波浪图配置* */startCanvas(){//录音波浪// this.oCanvas = document.getElementById('canvas');// this.ctx = this.oCanvas.getContext("2d");// //播放波浪// this.pCanvas = document.getElementById('playChart');// this.pCtx = this.pCanvas.getContext("2d");},/***  录音的具体操作功能* */// 开始录音startRecorder () {recorder.start().then(() => {// this.drawRecord();//开始绘制图片uni.showToast({title: '开始录音',})}, (error) => {// 出错了uni.showToast({title: `${error.name} : ${error.message}`,})console.log(`${error.name} : ${error.message}`);});},// 继续录音resumeRecorder () {recorder.resume()},// 暂停录音pauseRecorder () {recorder.pause();this.drawRecordId && cancelAnimationFrame(this.drawRecordId);this.drawRecordId = null;},// 结束录音stopRecorder () {recorder.stop()uni.showToast({title: '结束录音',})this.drawRecordId && cancelAnimationFrame(this.drawRecordId);this.drawRecordId = null;},// 录音播放playRecorder () {recorder.play();uni.showToast({title: '录音播放',})// this.drawPlay();//绘制波浪图},// 暂停录音播放pausePlayRecorder () {recorder.pausePlay()},// 恢复录音播放resumePlayRecorder () {recorder.resumePlay();this.drawPlay();//绘制波浪图},// 停止录音播放stopPlayRecorder () {recorder.stopPlay();},// 销毁录音destroyRecorder () {recorder.destroy().then(function() {recorder = null;this.drawRecordId && cancelAnimationFrame(this.drawRecordId);this.drawRecordId = null;});},/***  获取录音文件* */getRecorder(){let toltime = recorder.duration;//录音总时长let fileSize = recorder.fileSize;//录音总大小//录音结束,获取取录音数据let PCMBlob = recorder.getPCMBlob();//获取 PCM 数据let wav = recorder.getWAVBlob();//获取 WAV 数据let channel = recorder.getChannelData();//获取左声道和右声道音频数据console.log(toltime);console.log(fileSize);// console.log(PCMBlob);console.log(wav);console.log(channel);console.log(recorder);},/***  下载录音文件* *///下载pcmdownPCM(){//这里传参进去的时文件名recorder.downloadPCM('新文件');},//下载wavdownWAV(){//这里传参进去的时文件名recorder.downloadWAV('新文件');},/***  获取麦克风权限* */getPermission(){Recorder.getPermission().then(() => {this.$Message.success('获取权限成功')uni.showToast({title: '没有找到您要查询的内容!'})}, (error) => {uni.showToast({title: `${error.name} : ${error.message}`,})console.log(`${error.name} : ${error.message}`);});},/*** 文件格式转换 wav-map3* */getMp3Data(){const mp3Blob = this.convertToMp3(recorder.getWAV());recorder.download(mp3Blob, 'recorder', 'mp3');},blobToFile(theBlob, fileName) {  //将blob转换为file  let file = new File([theBlob], fileName, {type: theBlob.type.split('/')[1], lastModified: Date.now()});  return file;  },convertToMp3(wavDataView) {// 获取wav头信息const wav = lamejs.WavHeader.readHeader(wavDataView); // 此处其实可以不用去读wav头信息,毕竟有对应的config配置const { channels, sampleRate } = wav;const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);// 获取左右通道数据const result = recorder.getChannelData()const buffer = [];const leftData = result.left && new Int16Array(result.left.buffer, 0, result.left.byteLength / 2);const rightData = result.right && new Int16Array(result.right.buffer, 0, result.right.byteLength / 2);const remaining = leftData.length + (rightData ? rightData.length : 0);const maxSamples = 1152;for (let i = 0; i < remaining; i += maxSamples) {const left = leftData.subarray(i, i + maxSamples);let right = null;let mp3buf = null;if (channels === 2) {right = rightData.subarray(i, i + maxSamples);mp3buf = mp3enc.encodeBuffer(left, right);} else {mp3buf = mp3enc.encodeBuffer(left);}if (mp3buf.length > 0) {buffer.push(mp3buf);}}const enc = mp3enc.flush();if (enc.length > 0) {buffer.push(enc);}return new Blob(buffer, { type: 'audio/mp3' });},/*** 绘制波浪图-录音* */drawRecord () {//        // 用requestAnimationFrame稳定60fps绘制//        this.drawRecordId = requestAnimationFrame(this.drawRecord);//        // 实时获取音频大小数据//        let dataArray = recorder.getRecordAnalyseData(),//            bufferLength = dataArray.length;//        // 填充背景色//        this.ctx.fillStyle = 'rgb(200, 200, 200)';//        this.ctx.fillRect(0, 0, this.oCanvas.width, this.oCanvas.height);//        // 设定波形绘制颜色//        this.ctx.lineWidth = 2;//        this.ctx.strokeStyle = 'rgb(0, 0, 0)';//        this.ctx.beginPath();//        var sliceWidth = this.oCanvas.width * 1.0 / bufferLength, // 一个点占多少位置,共有bufferLength个点要绘制//                x = 0;          // 绘制点的x轴位置//        for (var i = 0; i < bufferLength; i++) {//          var v = dataArray[i] / 128.0;//          var y = v * this.oCanvas.height / 2;//          if (i === 0) {//            // 第一个点//            this.ctx.moveTo(x, y);//          } else {//            // 剩余的点//            this.ctx.lineTo(x, y);//          }//          // 依次平移,绘制所有点//          x += sliceWidth;//        }//        this.ctx.lineTo(this.oCanvas.width, this.oCanvas.height / 2);//        this.ctx.stroke();},/*** 绘制波浪图-播放* */drawPlay () {//        // 用requestAnimationFrame稳定60fps绘制//        this.drawPlayId = requestAnimationFrame(this.drawPlay);//        // 实时获取音频大小数据//        let dataArray = recorder.getPlayAnalyseData(),//                bufferLength = dataArray.length;//        // 填充背景色//        this.pCtx.fillStyle = 'rgb(200, 200, 200)';//        this.pCtx.fillRect(0, 0, this.pCanvas.width, this.pCanvas.height);//        // 设定波形绘制颜色//        this.pCtx.lineWidth = 2;//        this.pCtx.strokeStyle = 'rgb(0, 0, 0)';//        this.pCtx.beginPath();//        var sliceWidth = this.pCanvas.width * 1.0 / bufferLength, // 一个点占多少位置,共有bufferLength个点要绘制//                x = 0;          // 绘制点的x轴位置//        for (var i = 0; i < bufferLength; i++) {//          var v = dataArray[i] / 128.0;//          var y = v * this.pCanvas.height / 2;//          if (i === 0) {//            // 第一个点//            this.pCtx.moveTo(x, y);//          } else {//            // 剩余的点//            this.pCtx.lineTo(x, y);//          }//          // 依次平移,绘制所有点//          x += sliceWidth;//        }//        this.pCtx.lineTo(this.pCanvas.width, this.pCanvas.height / 2);//        this.pCtx.stroke();},uploadWav(){let file = this.blobToFile(recorder.getWAVBlob(),"1.wav")console.log("开始上传");console.log(file);uni.uploadFile({header: {},url: uni.getStorageSync('BaseUrl') +'/file/upload',// filePath: file,file:file,name: 'file',formData: {'user': 'test'},success: (uploadFileRes) => {console.log('/sys/common/static/scott/pic/' +JSON.parse(uploadFileRes.data).result);},complete: () => {},fail: (res) => {console.log(res)}})}},}
</script><style lang='less' scoped></style>
  1. 运行

  2. 注意,调试环境这里会报错,所以开始解决报错问题:
    报错:error:浏览器不支持getUserMedia !


这里使用的chrome浏览器测试的,所以我们在chrome浏览器当中输入:

chrome://flags/#unsafely-treat-insecure-origin-as-secure

输入你的本地网址,改为enabled,选择重启浏览器按钮【生产环境当中由于是使用域名进行访问,所以就不会报错。】

原因及原理:使用js-audio-recorder报浏览器不支持getUserMedia

  1. 调试正常:(大前提你的电脑要求插入耳机/喇叭和麦克风【耳麦一体的需要转换为双插头】否则会报找不到设备)



错误指引:

1. NotFoundError : Requested device not found


原因:没插麦克风
解决:插入麦克风并调整为音频主输入

2. 手机扫码显示不出来


原因:使用了localhost:8080作为url
解决:使用ip地址或者域名访问

3. 手机上访问不到或者无法测试(微信扫码也不行)

原因:只有谷歌内核的浏览器可以如此设置,想在手机上测试或使用请下载安卓版的谷歌浏览器或者使用域名访问。


插件和原理解说

插件官网(git)
https://github.com/2fps/recorder

文档
http://recorder.api.zhuyuntao.cn/Recorder/start.html

demo
https://recorder.zhuyuntao.cn/

uniapp文件上传函数解说
https://uniapp.dcloud.net.cn/api/request/network-file.html

uniapp中使用网页录音并上传声音文件(发语音)——js-audio-recorder的使用【伸手党福利】相关推荐

  1. java 网页 录音_Java+FlashWavRecorder实现网页录音并上传

    [注意] [前言] 肯定有需求要网页录音,并且要上传.这奇葩需求. 然后找到了FlashWavRecorder, [原始版本号] 1.下载 在上面的地址下载zip解压之后,目录里面有个index.ht ...

  2. Git中.gitignore的配置(git上传忽略文件/文件夹)

    在实际开发过程中,我们很多项目都需要使用git工具进行代码的拉取和提交等操作.但项目由于环境配置和打包等操作生成了一些不必要上传的文件夹或者一些我们自定义的文件不需要上传,这时候我们需要去配置.git ...

  3. js如何上传大文件到服务器,js将文件上传到远程服务器

    js将文件上传到远程服务器 内容精选 换一换 将文件上传至Windows云服务器一般会采用MSTSC远程桌面连接的方式.本节为您介绍本地Windows计算机通过远程桌面连接,上传文件至Windows云 ...

  4. go语言接收html上传的文件,html5原生js拖拽上传(golang版)

    package main import ("fmt" "io" "net/http" "os")const( uploa ...

  5. spring-boot、jeecg-boot中解除shiro限制了上传视频文件,以及设置上传文件大小的限制

    一.最近用jeecg-boot做视频上传功能时发现图片可以传,视频不可以传,想到了应该是shiro限制问题,刚开始找了半天也没找到配置文件. 最后找到了,是在config的包下面的ShiroConfi ...

  6. 快速列出windows10中所有已安装的应用列表(兼容win11)(长期有效)【伸手党福利】

    使用绿色安全的卸载软件geek (也可以百度下载) [单文件绿色版] 可以导出html形式的 导出后的效果 亲测可用 现将绿色版(中文的)和官网最新版(默认英文的)贡献给大家 由于csdn限制,发小白 ...

  7. JavaWeb:上传下载文件

    1. 文件上传概述 1.1 文件上传的作用 例如网络硬盘!就是用来上传下载文件的. 在智联招聘上填写一个完整的简历还需要上传照片呢. 1.2 文件上传对页面的要求 上传文件的要求比较多,需要记一下: ...

  8. 帆软报表决策系统 上传excel文件

    这个属于二次开发的,比如我要在决策系统中开发一个功能,上传excel文件或者其他文件, 首先前端使用fineUI: BI.File1 = BI.inherit(BI.File,{_defaultCon ...

  9. linux 传文件夹,linux下上传下载文件夹的方法

    Linux下目录复制:本机->远程服务器 scp -r /home/shaoxiaohu/test1 zhidao@192.168.0.1:/home/test2 test1为源目录,test2 ...

  10. [231]linux下怎么样上传下载文件夹

    Linux下目录复制:本机->远程服务器 scp -r /home/shaoxiaohu/test1 zhidao@192.168.0.1:/home/test2 test1为源目录,test2 ...

最新文章

  1. hdu6376 度度熊剪纸条 思维
  2. 新书问答:Company-Wide Agility
  3. Mac 完全卸载 Java
  4. 基于前后端分离实现阿里云对象存储小案例
  5. DataWhale sklearn学习笔记(一)
  6. Java黑皮书课后题第7章:7.27(相同的数组)如果两个数组list1和list2的内容相同,认为相同(不是完全相同)。编写一个测试程序,提示用户输入两个整数列表,然后显示这两个列表是否相同
  7. EEPlat的元模型体系
  8. AndroidUI 控件命名格式
  9. 设计php框架_PHP微型框架设计
  10. 指针常量、常量指针和常量指针常量
  11. python课程价格-python课程价格
  12. 190728每日一句 不经一番寒彻骨 怎得梅花扑鼻香,一个人怎样保持激情去奋斗?
  13. c语言小程序 祝你生日快乐!
  14. 带后台管理的超酷jquery+ajax幻灯相册php源码,js幻灯片轮播代码_js源码_js鼠标滚动翻页...
  15. Android JTT 808-2011 道路运输车辆卫星定位系统终端通讯协议及数据格式
  16. switch服务器维护时间2020,switch pro什么时候出,2020性能加强版switch发布时间
  17. 汽车行业如何玩转“Web3.0”?智己汽车“原石谷”开启区块链应用新场景!
  18. 2021-09-30 cannot locate default realm
  19. pptx---基础概念解释
  20. 微模块动环检测(动环监控模块)

热门文章

  1. python制作好看的界面_python漂亮界面
  2. 〇、什么是全栈工程师
  3. iTunes只能装C盘吗_为什么电脑软件喜欢默认安装在C盘呢?
  4. 域名到期查询如何查看?有什么新方法吗?
  5. Selenium元素定位神器工具谷歌浏览器插件-SelectorsHub介绍、安装和使用
  6. V$LOGMNR_CONTENTS字段含义
  7. Python实现飞机大战
  8. Python 错误重试库 tenacity retry
  9. 银行贷款违约风险预测
  10. 【Windows】使用Window自带远程桌面远程