小程序錄音功能實現
發表時(shí)間:2021-3-15
發布人(rén):融晨科技
浏覽次數:63
前言
在(zài)開發小程序過程中,有一個(gè)實現錄音功能并播放錄音,将錄音上(shàng)傳至服務器的(de)需求。開發過程中使用了(le/liǎo)Taro框架,錄音功能通過 Taro.getRecorderManager()
接口實現,上(shàng)傳錄音至服務器通過 Taro.uploadFile
接口實現,播放錄音使用 Taro.createInnerAudioContext()
接口實現。下面就(jiù)詳細介紹整個(gè)流程是(shì)如何實現的(de)。
小程序錄音
首先獲取錄音管理器模塊:
const recorderManager = Taro.getRecorderManager();
複制代碼
在(zài)組件挂載完畢時(shí)注冊錄音監聽事件:
useEffect(() => {
// 監聽錄音開始
recorderManager.onStart(() => {
console.log('開始錄音');
});
// 監聽錄音暫停
recorderManager.onPause(() => {
console.log('暫停錄音');
});
// 監聽錄音繼續
recorderManager.onResume(() => {
console.log('繼續錄音');
});
// 監聽錄音停止
recorderManager.onStop((res) => {
if (res.duration < 1000) {
Taro.showToast({
title: '錄音時(shí)間太短',
duration: 1000,
icon: 'none',
});
} else {
console.log('停止錄音');
fileUpload(res.tempFilePath);
}
});
recorderManager.onError(() => {
Taro.showToast({
title: '錄音失敗!',
duration: 1000,
icon: 'none',
});
});
}, []);
複制代碼
在(zài)錄音 onStop
的(de)回調函數中,我們可以(yǐ)獲取到(dào)錄音的(de)臨時(shí)地(dì / de)址 res.tempFilePath
,但這(zhè)個(gè)地(dì / de)址是(shì)有有效期限的(de),所以(yǐ)我們需要(yào / yāo)将這(zhè)個(gè)錄音上(shàng)傳至服務器後台,進行保存,後續才能正常使用。
onStop
回調函數中我們調用了(le/liǎo) fileUpload
函數實現文件上(shàng)傳, fileUpload
函數的(de)實現如下:
const fileUpload = (tempFilePath) => {
Taro.uploadFile({
url: 'http://127.0.0.1:7001/record', // 服務器地(dì / de)址
filePath: tempFilePath,
name: 'file', // 這(zhè)個(gè)随便填
header: {
'content-type': 'multipart/form-data', // 格式必須是(shì)這(zhè)個(gè)
Authorization: Taro.getStorageSync('token'),
},
// formData用于(yú)傳輸除文件以(yǐ)外的(de)一些信息
formData: {
record_name: '朗誦作品',
poem_id: poemInfo.id,
category: poemInfo.category,
},
success: (res) => {
console.log(res);
const url = res.data;
playAudio(url); // 播放錄音
},
fail: (error) => {
console.log('failed!');
console.error(error);
},
});
};
複制代碼
需要(yào / yāo)注意的(de)點是(shì): header
中的(de) content-type
必須是(shì) multipart/form-data
。
錄音事件的(de)處理
第一次點擊 handleClick
就(jiù)會觸發開始錄音,之(zhī)後會通過當前狀态判斷是(shì)暫停錄音還是(shì)繼續錄音。 handleComplete
用于(yú)停止錄音。
const handleClick = () => {
const curPause = pause;
setPause(!curPause);
if (firstRecord) {
setfirstRecord(false);
recorderManager.start({
duration: 60000,
sampleRate: 44100,
numberOfChannels: 1,
encodeBitRate: 192000,
format: 'mp3',
frameSize: 50,
});
Taro.showToast({
title: '開始錄音',
duration: 1000,
icon: 'none',
});
} else {
if (curPause) {
recorderManager.pause(); // 暫停錄音
} else {
recorderManager.resume(); // 繼續錄音
}
}
};
const handleComplete = () => {
recorderManager.stop(); // 停止錄音
};
複制代碼
後台實現錄音存儲并返回錄音地(dì / de)址
網上(shàng)大(dà)多數博客都沒有涉及這(zhè)塊内容,下面就(jiù)介紹一下如何實現,後台框架我用的(de)是(shì)阿裏的(de) egg.js
。
文件上(shàng)傳需要(yào / yāo)配置的(de)東西可見官方文檔: egg.js文件上(shàng)傳 。我們這(zhè)裏使用它的(de)第一種 File
模式來(lái)實現。
因爲(wéi / wèi) egg.js
框架内置了(le/liǎo) Multipart
插件,可以(yǐ)解析上(shàng)傳的(de) multipart/form-data
類型的(de)數據。
首先,現在(zài)配置文件 config.default.js
中寫入 multipart
配置:
module.exports = (app) => {
const config = (exports = {});
...
config.multipart = {
mode: 'file',
fileSize: '50mb',
}
...
return {
...config,
...userConfig,
};
};
複制代碼
然後,在(zài) router.js
中定義路由:
// 提交錄音
router.post('/record', auth, controller.record.postRecord);
複制代碼
在(zài) controller
目錄下定義 record.js
文件寫入如下内容:
const Controller = require('egg').Controller;
class RecordController extends Controller {
async postRecord() {
const { ctx } = this;
const file = ctx.request.files[0];
const { record_name, poem_id, category } = ctx.request.body;
const res = await ctx.service.record.postRecord(file, record_name, poem_id, category);
ctx.body = res;
}
}
module.exports = RecordController;
複制代碼
在(zài) service
目錄下定義 record.js
寫入具體實現:
const Service = require('egg').Service;
let OSS = require('ali-oss');
let aliInfo = {
// https://help.aliyun.com/document_detail/31837.html
region: 'oss-cn-guangzhou',
bucket: 'poem-mini-program',
accessKeyId: 'xxx', // 填入阿裏雲的(de)accessKeyId
accessKeySecret: 'xxx', // 填入阿裏雲的(de)accessKeySecret
};
let client = new OSS(aliInfo);
class RecordService extends Service {
async postRecord(file, record_name, poem_id, category) {
const url = await this.uploadOSS(file);
await this.updateRecord(url, record_name, poem_id, category);
return url;
}
async uploadOSS(file) {
const { ctx } = this;
let result;
try {
// 處理文件,比如上(shàng)傳到(dào)雲端
result = await client.put(file.filename, file.filepath);
} finally {
// 需要(yào / yāo)删除臨時(shí)文件
await ctx.cleanupRequestFiles();
}
return result.url;
}
async updateRecord(url, record_name, poem_id, category) {
const { ctx } = this;
console.log('從ctx.locals中取openid');
console.log(ctx.locals.openid);
const openid = ctx.locals.openid;
// 将用戶信息記錄到(dào)數據庫中
const res = await ctx.model.Record.create({
record_name: record_name,
record_url: url,
poem_id: poem_id,
category: category,
openid: openid,
});
}
}
module.exports = RecordService;
複制代碼
這(zhè)裏需要(yào / yāo)注意的(de)是(shì):
- 需要(yào / yāo)注冊阿裏雲賬号,并在(zài)對象存儲那裏新建一個(gè)存儲桶用于(yú)存放音頻,也(yě)就(jiù)是(shì)雲存儲的(de)實現。
- 需要(yào / yāo)安裝
ali-oss
npm包,用于(yú)連接阿裏雲對象存儲。在(zài)後台接收到(dào)前端上(shàng)傳的(de)臨時(shí)文件後,就(jiù)會将音頻上(shàng)傳至阿裏雲對象存儲中(client.put
)。
播放錄音
細心的(de)小夥伴可以(yǐ)注意到(dào)在(zài)使用 Taro.uploadFile
接口上(shàng)傳錄音後,在(zài) success
回調中調用了(le/liǎo) playAudio
函數用于(yú)播放音頻,接下來(lái)講一下播放音頻是(shì)如何實現的(de)。
首先,使用 Taro.createInnerAudioContext
獲取 audio
的(de)上(shàng)下文對象:
const innerAudioText = Taro.createInnerAudioContext();
複制代碼
和(hé / huò)錄音一樣,在(zài)組件挂載完成時(shí),注冊監聽事件:
useEffect(() => {
innerAudioText.onPlay(() => {
console.log('開始播放');
});
innerAudioText.onError((e) => {
console.log('播放異常');
console.log(e);
});
}, []);
複制代碼
在(zài)錄音文件上(shàng)傳成功後,調用 playAudio
方法用于(yú)播放錄音:
const playAudio = (url) => {
innerAudioText.autoplay = true;
innerAudioText.src = http://www.wxapp-union.com/url;
};
複制代碼
在(zài) src
被賦予值的(de)時(shí)候,錄音就(jiù)會開始播放。
總結
以(yǐ)上(shàng)就(jiù)是(shì)整個(gè)錄音功能和(hé / huò)錄音播放功能實現的(de)整個(gè)流程,如有疑問歡迎大(dà)家一起交流。