提示:webRTC要https协议或者127.0.0.1或localhost访问,出于安全性考虑

首先要知道nodejs怎么上传文件,如果不会,请移步这里
没有multiparty的自行安装一下

npm install multiparty

上传文件会了之后这个就简单了,recorder.js就是基于webRTC做的

~~

~~

~~

~~看目录

nodejs语音识别库 nodejs 录音_webRTC


recorder.js的例子只是一个前端的录音和播放等,没涉及到服务端功能,给服务端传啥啊(其实是自己没发现而已),这不行啊,于是就开始读源码,准备改改让其符合自己的需求,结果读完发现,自己要的东西都有了,没必要改了,其实也没啥好改的了,只需要将录音得到的东西传给服务端不就哦了

突然间发现,其实就是一个录音功能和上传文件并返回文件地址的一个功能,感觉没啥好讲的了,不信你看哈:

上传的ajax封装好了,只需要调用就行,录音功能recorder.js也封装好了,又是调用,里面也有例子,算了,把我写好的例子放这儿吧

//app.js
app.use('/users', usersRouter); //这里添加一个路由,录音完成要上传用的路由

//router/users.js //这里是nodejs服务端保存文件用的 ~em mmm~ 接口
var express = require('express');
var path = require('path')
var router = express.Router();
var multiparty = require('multiparty');
var util = require('util');
var fs = require('fs');
//这是直接借用了之前的例子,反正报错和不报错都能走到页面,就它了,不要在意细节~~~
router.get('/',function(req,res,next){
    db.query("select * from user",function(err,rows){
        if(err){
            res.render("users",{title:'用户列表',datas:[],s_name:''});
        }else{
            res.render("users",{title:'用户列表',datas:rows,s_name:''});
        }
    })
});

router.post('/loadwav', function (req, res, next) {
	let storagePath = __dirname + '/../public/files/'
	var form = new multiparty.Form({ uploadDir: storagePath });
	form.parse(req, function (err, fields, files) {
		console.log(files, fields);
		if (err) {
			console.log('parse error: ' + err);
			res.status(200).json({ success:false,msg: '上传失败', err, fileUrl: '' });
			res.end();
		} else {
			let inputFile = files.file[0],myDate = new Date();
			inputFile.originalFilename = 'record'+myDate.getTime()+''+parseInt(Math.random()*1000) + '.' + fields.type
			let tmpPath = inputFile.path;
			let backUrl = 'files/'+inputFile.originalFilename;
			// let finalPath = storagePath + inputFile.originalFilename;
			let finalPath = __dirname + '/../public/' + backUrl;
			//移动并重命名为真实文件名
			fs.rename(tmpPath, finalPath, function (err) {
				if (err) {
					console.log('rename error: ' + err);
				} else {
					console.log('rename ok');
				}
			});
			res.status(200).json({ success:true,msg: '上传成功', fileUrl: backUrl, err: '' });
			res.end();
		}

		// res.writeHead(200, { 'content-type': 'application/json;charset=utf-8' });
		// res.write('received upload:\n\n');
	})
});
module.exports = router;

下面这个是views/user.ejs模板,没写样式,注意看重点,不要在意细节

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%= title %></title>
</head>
<body>
    <ul id="audioBox"></ul>
    <input type="button" value="录音开启" id="startRecord" />
    <input type="button" value="暂停" id="pauseRecord" />
    <input type="button" value="恢复" id="resumeRecord" />
    <input type="button" value="录音停止" id="endRecord" />
    <input type="button" value="播放录音" id="playRecord" />
    <button id="wavBlobBtn">上传wavblob语音对象</button>
    <h3>
        下载
    </h3>
    <input type="button" value="下载pcm" id="downloadPCM" />
    <input type="button" value="下载wav" id="downloadWAV" />
    <h3>
        录音时长:<span id="time">0</span>s
    </h3>
    <h3>
        声音波形图如下:
    </h3>
    <canvas id="canvas"></canvas>
    <h3>
        加载播放:
    </h3>
    <input type="file" value="加载文件播放" id="uploadAudio" />
    <script src="javascripts/ajax.js"></script>
    <script src="javascripts/recorder.js"></script>
    <script>
        var oDiv = document.getElementById('box'),
        audio = document.getElementById('audio'),
        oTime = document.getElementById('time'),
        recorder = null,
        oCanvas = document.getElementById("canvas"),        // 显示波形的canvas
        ctx = oCanvas.getContext("2d"),
        drawRecordId = null,
        audiObj = document.getElementById('audioTag'),
        ulObj = document.getElementById('audioBox');
        // 按钮事件绑定
        document.getElementById('startRecord').addEventListener('click', startRecord);
        document.getElementById('pauseRecord').addEventListener('click', pauseRecord);
        document.getElementById('resumeRecord').addEventListener('click', resumeRecord);
        document.getElementById('endRecord').addEventListener('click', endRecord);
        document.getElementById('playRecord').addEventListener('click', playRecord);
        document.getElementById('downloadPCM').addEventListener('click', downloadPCM);
        document.getElementById('downloadWAV').addEventListener('click', downloadWAV);
        document.getElementById('uploadAudio').addEventListener('change', uploadAudio);
        document.getElementById('wavBlobBtn').addEventListener('click', getWavBlob);
        // canvas背景初始化
        initCanvasBg()
        // 开始录音
        function startRecord() {
            if (!recorder) {
                recorder = new Recorder({
                    // 以下是默认配置
                    sampleBits: 16,
                    // sampleRate: 浏览器默认的输入采样率,
                    numChannels: 1,
                });
                recorder.onprocess = function(duration) {
                    oTime.innerText = duration;
                }
            }
            recorder.start();
            // 开始绘制canvas
            drawRecord();
        }
        // 暂停录音
        function pauseRecord() {
            recorder && recorder.pause();
        }
        // 恢复录音
        function resumeRecord() {
            recorder && recorder.resume();
        }
        // 结束录音
        function endRecord (e) {
            recorder && recorder.stop();
            drawRecordId && cancelAnimationFrame(drawRecordId);
            drawRecordId = null;
        }
        // 播放录音
        function playRecord() {
            recorder && recorder.play();
            drawRecordId && cancelAnimationFrame(drawRecordId);
            drawRecordId = null;
        }
        // 下载pcm
        function downloadPCM() {
            recorder && recorder.downloadPCM();
        }
        // 下载wav
        function downloadWAV() {
            recorder && recorder.downloadWAV();
        }
        // canvas波形绘制函数
        function drawRecord() {
            // 用requestAnimationFrame稳定60fps绘制
            drawRecordId = requestAnimationFrame(drawRecord);
            // 实时获取音频大小数据
            var dataArray = recorder.getRecordAnalyseData(),
                bufferLength = dataArray.length;
            // 填充背景色
            ctx.fillStyle = 'rgb(200, 200, 200)';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // 设定波形绘制颜色
            ctx.lineWidth = 2;
            ctx.strokeStyle = 'rgb(0, 0, 0)';
            
            ctx.beginPath();
            
            var sliceWidth = canvas.width * 1.0 / bufferLength, // 一个点占多少位置,共有bufferLength个点要绘制
                x = 0;          // 绘制点的x轴位置
            for (var i = 0; i < bufferLength; i++) {
                var v = dataArray[i] / 128.0;
                var y = v * canvas.height / 2;
            
                if (i === 0) {
                    // 第一个点
                    ctx.moveTo(x, y);
                } else {
                    // 剩余的点
                    ctx.lineTo(x, y);
                }
                // 依次平移,绘制所有点
                x += sliceWidth;
            }
            
            ctx.lineTo(canvas.width, canvas.height / 2);
            ctx.stroke();
        }
        // canvas背景初始化
        function initCanvasBg() {
            ctx.fillStyle = 'rgb(200, 200, 200)';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }
        // 加载音频文件并播放
        function uploadAudio(e) {
            Recorder.playAudio(this.files[0]);
        }
        //获取wavblob对象
        function getWavBlob(){
            recorder && recorder.stop();
            drawRecordId && cancelAnimationFrame(drawRecordId);
            drawRecordId = null;
            let url = 'http://localhost:3000/users/loadwav'; //上传地址
            let wavBlob = recorder.getWAVBlob();
            let formData = new FormData();
            formData.append('file',wavBlob);
            formData.append('name','recorder');
            formData.append('type','wav');
            new Promise((resolve,reject)=>{
                Ajax({
                    url,
                    data:formData,
                    resolve,
                    reject,
                    headerType:2
                })
            }).then(function(resp){
                if(resp.success){
                    let li = document.createElement("li");
                    let audio = document.createElement("audio");
                    li.appendChild(audio);
                    audio.setAttribute("controls","controls");
                    audio.src = resp.fileUrl;
                    ulObj.appendChild(li);
                }else{
                    alert("发送语音失败")
                }
            }).catch(function(error){
                console.log(error);
            })
        }
    </script>
</body>
</html>

超级low的demo

nodejs语音识别库 nodejs 录音_nodejs语音识别库_02


总结:写好了以后发现,挺简单挺烂的一个demo,将就看吧,这就是用来实现功能的,然后可以拿到自己的项目里,那时候才是花里胡哨的页面的时候,不喜勿喷哦 (* ̄3 ̄)╭

2019-06-21更新:
追加vue版本demo:
添加对应demo的router就不一 一叙说了,直接看例子,对了,这里的vue版本本地调试还是用的上面的后台

  1. 涉及到跨域问题,上面的服务端app.js添加CORS代码:
app.all('*', function(req, res, next) {
  console.log('req.url',req.url);
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "X-Requested-With");
  res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
  res.header("Content-Type", "application/json;charset=utf-8");
  next();
});
  1. vue添加本地反向代理 如不会请看这里:
'/users': {
	changeOrigin: true,
	target: 'http://localhost:3000', //后台反向代理到的地址
	pathRewrite: {
		'^users': ''
	}
}
  1. voice组件,demo专用
<template>
    <div class="voice">
        <ul class="msg-box">
            <li v-for="(item,index) of voiceList" :key="index">
                <audio :src="item.fileUrl" controls></audio>
            </li>
        </ul>
        <div class="btn-box">
            <p class="time" v-show="canvasFlag">{{time}}s</p>
            <canvas id="canvas" v-show="canvasFlag"></canvas>
            <el-button type="primary" size="small" @click="startRecord">开始录音</el-button>
            <el-button type="primary" size="small" @click="sendVoice">发送录音</el-button>
        </div>
    </div>
</template>

<script>
import Recorder from "@/plugins/recorder.js";
export default {
    data() {
        return {
            visible: false,
            msgList: [],
            time: 0,
            recorder: null,
            oCanvas: null,
            ctx: null,
            canvasFlag: false,
            drawRecordId: null
        };
    },
    mounted() {
        this.initCanvasBg();
    },
    computed: {
        voiceList() { //这个是获取到语音消息列表之后,利用计算属性给每个语音地址转换成绝对路径地址
            let tmpList = [];
            tmpList = JSON.parse(JSON.stringify(this.msgList));
            tmpList.forEach((el, i) => {
                el.fileUrl = 'http://localhost:3000/'+el.fileUrl;
            })
            return tmpList;
        }
    },
    methods: {
        startRecord() { //开始录音
            this.canvasFlag = true;
            if (!this.recorder) {
                this.recorder = new Recorder({
                    // 以下是默认配置
                    sampleBits: 16,
                    numChannels: 1
                });
                this.recorder.onprocess = (duration)=> {
                    this.time = duration.toFixed(4);
                };
            }
            this.recorder.start();
            // 开始绘制canvas
            this.drawRecord();
        },
        sendVoice() { //发送语音消息
            this.canvasFlag = false;
            this.recorder && this.recorder.stop();
            this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
            this.drawRecordId = null;
            let url = "/users/loadwav"; //上传地址
            let wavBlob = this.recorder.getWAVBlob();
            let data = new FormData();
            data.append("file", wavBlob);
            data.append("type", "wav");
            this.$axios
                .post(url, data, {
                    headers: { "Content-Type": "multipart/form-data" }
                })
                .then(res => {
                    if(res.data.success){
                        let tmpObj = {};
                        tmpObj.fileUrl = res.data.fileUrl;
                        this.msgList.push(tmpObj);
                    }else{
                        this.$message({
                            message: "消息发送失败,请重试",
                            type: "warning"
                        });
                    }
                })
                .catch(err => {
                    this.$message({
                        message: "消息发送失败,请重试",
                        type: "warning"
                    });
                });
        },
        initCanvasBg() {
            this.oCanvas = document.getElementById("canvas"); // 显示波形的canvas
            this.ctx = this.oCanvas.getContext("2d");
            this.ctx.fillStyle = "#f5f5f5";
            this.ctx.fillRect(0, 0, this.oCanvas.width, this.oCanvas.height);
        },
        drawRecord() {
            // 用requestAnimationFrame稳定60fps绘制
            this.drawRecordId = requestAnimationFrame(this.drawRecord);
            // 实时获取音频大小数据
            let dataArray = this.recorder.getRecordAnalyseData(),
                bufferLength = dataArray.length;
            // 填充背景色
            this.ctx.fillStyle = "#f5f5f5";
            this.ctx.fillRect(0, 0, this.oCanvas.width, this.oCanvas.height);

            // 设定波形绘制颜色
            this.ctx.lineWidth = 2;
            this.ctx.strokeStyle = "rgba(0, 0, 0, 0.3)";

            this.ctx.beginPath();

            let sliceWidth = (this.oCanvas.width * 1.0) / bufferLength, // 一个点占多少位置,共有bufferLength个点要绘制
                x = 0; // 绘制点的x轴位置
            for (let i = 0; i < bufferLength; i++) {
                let v = dataArray[i] / 128.0;
                let 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();
        }
    }
};
</script>

<style lang="less" scoped>
.voice {
    width: 100%;
    height: 100%;
    background: #ffffff;
    padding: 10px;
    .msg-box {
        min-height: 150px;
        max-height: calc(100% - 80px);
        width: 500px;
        overflow-y: auto;
        border: 1px solid #333333;
        margin-bottom: 15px;
    }
    .btn-box {
        position: relative;
        #canvas {
            position: absolute;
            bottom: 40px;
            z-index: 1;
        }
        .time {
            position: absolute;
            bottom: 50px;
            left: 15px;
            z-index: 2;
        }
    }
}
</style>

看一下今天这个例子截图,比昨天的漂亮多了

nodejs语音识别库 nodejs 录音_nodejs语音识别库_03