当前位置:网站首页>uniapp中使用网页录音并上传声音文件(发语音)——js-audio-recorder的使用【伸手党福利】
uniapp中使用网页录音并上传声音文件(发语音)——js-audio-recorder的使用【伸手党福利】
2022-08-09 17:22:00 【三个人工作室】
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编写——前期准备:
- 安装插件
yarn add js-audio-recorder
如果需要转mp3加一句:
yarn add lamejs
- 建立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,默认是16
sampleRate: 48000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
numChannels: 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);
},
/** * 下载录音文件 * */
//下载pcm
downPCM(){
//这里传参进去的时文件名
recorder.downloadPCM('新文件');
},
//下载wav
downWAV(){
//这里传参进去的时文件名
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>
运行

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

这里使用的chrome浏览器测试的,所以我们在chrome浏览器当中输入:
chrome://flags/#unsafely-treat-insecure-origin-as-secure
输入你的本地网址,改为enabled,选择重启浏览器按钮【生产环境当中由于是使用域名进行访问,所以就不会报错。】
原因及原理:使用js-audio-recorder报浏览器不支持getUserMedia
- 调试正常:(大前提你的电脑要求插入耳机/喇叭和麦克风【耳麦一体的需要转换为双插头】否则会报找不到设备)




错误指引:
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
边栏推荐
猜你喜欢

第三方bean使用ConfigurationProperties注解获取yml配置文件数据 & 获取yml配置文件数据的校验

Engaged in software testing for a year, only basic functional testing, how to further study?

一文深入了解 Hybrid 的实现原理

自学软件测试,学到什么程度可以出去找工作啊?

Li Yuanyuan: iMetaLab Suite metaproteomics data analysis and visualization (video + PPT)

艺术与科技的狂欢,云端XR支撑阿那亚2022砂之盒沉浸艺术季

Logic unauthorized and horizontal and vertical unauthorized payment tampering, verification code bypass, interface

混动产品助力,自主SUV市场格局迎来新篇章

ARM 汇编基础

The strongest distributed lock tool: Redisson
随机推荐
win10 uwp 手动锁Bitlocker
Experience far more than Hue, this is the favorite SQL tool for technicians
测试开发是什么,为什么现在这么吃香?
win10 uwp 绑定静态属性
The senior told me that the MySQL of the big factory is connected through SSH
OpenCV 轮廓 —— 轮廓匹配
loadrunner脚本--参数化
mysql generates random name, mobile number, date
win10 uwp 装机必备应用 含源代码
Can't install the Vmware virtual machine on the Ark Kai server?
秋招面试大厂总被刷下来,你这样做保准你事半功倍!
LeetCode笔记:Weekly Contest 305
Discuz! Forum program installation + template configuration tutorial
LeetCode笔记:Biweekly Contest 84
Flink on Yarn
动态RDLC报表(三)
对数学直观、感性的认知是理解数学、喜爱数学的必经之路,这本书做到了!
搭建Zabbix监控系统
Logic unauthorized and horizontal and vertical unauthorized payment tampering, verification code bypass, interface
.NET 6学习笔记(4)——解决VS2022中Nullable警告