记录--uniapp 安卓端实现录音功能,保存为amr/mp3文件
🧑💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣
功能实现需要用到MediaRecorder、navigator.mediaDevices.getUserMedia、Blob等API,uniapp App端不支持,需要借助renderjs来实现
实现逻辑
- 通过navigator.mediaDevices.getUserMedia调用设备麦克风,获取音频流
- 使用MediaRecorder录制音频数据
- 用Blob、FileReader将音频数据转为转为base64格式
- 调用fs.root.getFile创建空白amr/mp3文件
- 通过h5plus调用安卓APIjava.io.FileOutputStream将数据保存进文件中
代码实现
获取音频流
申请媒体权限
首先需要提前申请下权限。App 端调用navigator.mediaDevices.getUserMedia时不像浏览器上会弹框申请权限,插件市场有官方提供的免费插件:App权限判断和提示 - DCloud 插件市场。
async onLoad() {
// 申请麦克风权限
let isRecord = await permision.requestAndroidPermission('android.permission.RECORD_AUDIO')
if (isRecord == -1) {
uni.showModal({
title: '提示',
content: '未获取到麦克风权限,可能导致应用运行出现问题',
confirmText: '去开启',
cancelText: "暂不开启",
success: res => {
if (res.confirm) {
// 跳转权限页面
permision.gotoAppPermissionSetting()
}
}
})
}
}
获取音频流代码
mediaRecorder.start(60 * 1000); 表示60秒执行一次ondataavailable,可用来实现分段录制。不传参数则只在stop时执行一次
<script lang="renderjs">
export default {
async startRecord() {
try {
let localStream = await navigator.mediaDevices.getUserMedia({
video: false,
audio: true,
});
let mediaRecorder = new MediaRecorder(localStream);
mediaRecorder.ondataavailable = event => {
const blob = new Blob([event.data]);
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = (e) => {
let base64str = e.target.result
// 调用外层script中的base64ToFile方法,将base64传入。
this.$ownerInstance.callMethod('base64ToFile', base64str)
}
}
// 录制停止
mediaRecorder.onstop = (e) => { }
mediaRecorder.start(60 * 1000); // 60秒执行一次ondataavailable,可实现分段录制,不传参数只在stop时执行一次
} catch (e) {
console.log('麦克风权限获取失败')
}
}
}
</script>
保存amr/mp3文件
文件保存在uniapp_save文件夹下可以通过uni.getSavedFileList方法获取到。
plus.io.PRIVATE_DOC 位置参考:内部存储>Android>data>io.dcloud.HBuilder>apps>HBuilder>doc。
<script>
export default {
base64ToFile(base64Str, fileName = `uniapp_save/${new Date().valueOf()}.amr`) {
// 去除base64前缀
var index = base64Str.indexOf(',')
var base64Str = base64Str.slice(index + 1, base64Str.length)
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
fs.root.getFile(fileName, {
create: true
}, function(entry) {
// 获得本地路径URL,file:///xxx/doc/1663062980631.amr
var fullPath = entry.fullPath;
var FileOutputStream = plus.android.importClass("java.io.FileOutputStream");
try {
function base64ToByteArray(base64Str) {
const binaryString = atob(base64Str);
const uint8Array = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
uint8Array[i] = binaryString.charCodeAt(i);
}
let arr = []
Array.from(uint8Array).map(num => {
arr.push(num >= 128 ? (num - 256) : num)
})
return arr;
}
var out = new FileOutputStream(fullPath);
let bytes = base64ToByteArray(base64Str);
if (bytes == null || bytes.length == 0) {
out.close();
uni.hideLoading();
uni.showModal({
title: "生成失败",
content: "nativeJS限制参数长度无法获取文件!",
showCancel: false
})
return
} else {
out.write(bytes);
out.close();
console.log(`保存成功,文件地址为:_doc/${fileName}`)
}
} catch (e) {
console.log(e.message);
}
})
})
}
}
</script>
参考资料
-
在base64转本地文件(如pdf,apk,音频等)如果base64长度过长 在 安卓中 Base64.decode(str, 0) 返回 null 的解决问题 - DCloud问答
-
HTML5+ API
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。