「微信小程序」小程序端录音、播放踩坑日记

您所在的位置:网站首页 pcm后缀文件 「微信小程序」小程序端录音、播放踩坑日记

「微信小程序」小程序端录音、播放踩坑日记

2023-09-30 05:51| 来源: 网络整理| 查看: 265

一、前言 开发背景:首次尝试小程序中实现录音、播放功能。 开发框架: taro 2.2.6 taro-ui 2.3.4 难点描述: 实现小程序录音、上传到后台 PC、IOS 和安卓端音频播放资源的地址,支持 mp3 下载链接

温馨提示:这篇文章重点介绍小程序的音频在各种环境录音和播放实践。适用对象:遇到小程序在 IOS 端无法播放音频的同学们和对小程序兼容性感兴趣的同学。

二、小程序录音、上传 2.1 注册事件监听

首先,介绍一下录音的部分。这里主要用到了小程序中的 wx.getRecorderManager() 模块部分。

直接放代码,感兴趣的可以去微信开发文档就了解下各种配置。

import Taro, { Component } from '@tarojs/taro' export default class Index extends Component { ... // 声明录音管理器模块 recorderManager = wx.getRecorderManager() componentDidMount() { // 抛出错误 recorderManager.onError(() => { Taro.showToast({ title: '录音失败!', duration: 1000, icon: 'none' }) }) // 录音结束时的处理 recorderManager.onStop(res => { if (res.duration < 1000) { Taro.showToast({ title: '录音时间太短', duration: 1000, icon: 'none' }) } else { // content 是存储录音结束后的数据结构,用于调试 this.setState({ content: res }) wx.saveFile({ tempFilePath: res.tempFilePath, success: result => { // 这里会调用一个文件上传的接口 this.fileUpload(result.savedFilePath) } }) } }) } fileUpload(tempFilePath) { Taro.uploadFile({ url: XXXApi, filePath: tempFilePath, name: 'file', header: { 'content-type': 'multipart/form-data', cookie: Taro.getStorageSync('cookie') // 上传需要单独处理 cookie }, formData: { method: 'POST' // 请求方式 }, success: res => { // 录音上传成功之后的处理 } }) } }

梳理一下:

在 componentDidMount 生命周期中,注册几个重要的事件。包括:监听录音错误事件和监听录音结束事件 在录音结束时,用 wx.savefile 将文件保存到本地 在 wx.savefile 成功的回调中,调用文件上传的接口,将文件上传到服务器。 2.2 实现录音事件处理函数

先看下 dom 节点部分:

上传语音 长按说话

其中就两个事件:handleRecordStart 和 handleRecordStop。他们分别是长按时触发和手指松开时触发。

简单实现:

// longpress (长按)时触发 handleRecordStart(e) { this.setState({ record: { // 修改录音数据结构,此时录音按钮样式会发生变化。 text: '松开保存', type: 'recording' } }) // 开始录音 this.recorderManager.start({ duration: 60000, sampleRate: 44100, numberOfChannels: 1, encodeBitRate: 192000, format: 'mp3', frameSize: 50 }) Taro.showToast({ title: '正在录音', duration: 60000, icon: 'none' }) } // touchend (手指松开)时触发 handleRecordStop() { // 复原在 start 方法中修改的录音的数据结构 this.setState({ record: { text: '长按录音', type: 'record' } }) // 结束录音、隐藏 Toast 提示框 wx.hideToast() // 结束录音 this.recorderManager.stop() }

这里用了一个 record 对象来记录录音的状态。

注意 recorderManager.start 方法的参数中, duration 指录音时长,这里设置为 60000 ms;format 值为 mp3,意思录音得到的音频文件为 mp3 格式。

温馨提示:最初开发没有设置成格式化为 mp3,导致后台同事增加了工作量(将 m4a 转换成 mp3),这里建议前端直接处理,很方便。

三、小程序端录音的播放 3.1 录音播放

说到音频播放,大家第一时间可能想到的是 Audio 标签,然后给其中的 src 属性动态赋值就好了。没错,PC 端确实是这样。但是小程序比较坑,如下图:

音频播放这里,我们选用了 wx.createInnerAudioContext() 接口。

温馨提示:如果音频上传到后台之后可以返回 .mp3 结尾的 url 链接(例如:http://47.104.167.164/faceVideo/result_2020_07_21_12_33_43.mp3),可以考虑直接利用 wx.createInnerAudioContext() 的 play() 方法实现播放。

由于部分原因,我们后台上传音频文件后,返回的链接是一个云文件 ID(指浏览器打开可以下载此 mp3 文件)。而且经过测试发现,安卓端可以直接播放,IOS 端直接播放没有声音。

然后,请教了一下我们组的架构师,决定将文件先下载下来,然后保存到手机本地,最后播放(经过测试方案可行)。

我们直接看代码:

// 小程序音频播放 api innerAudioContext = wx.createInnerAudioContext() // 下载音频文件 downloadFile() { const FileSystemManager = wx.getFileSystemManager() const { voiceUrl } = this.state wx.downloadFile({ url: voiceUrl, header: { 'Content-type': 'audio/mp3' }, success: res => { // 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容 if (res.statusCode === 200) { FileSystemManager.saveFile({ tempFilePath: res.tempFilePath, // 文件地址为手机本地 filePath: `${wx.env.USER_DATA_PATH}/${new Date().getTime()}.mp3`, success: result => { if (result.errMsg == 'saveFile:ok') { this.registerAudioContext(result.savedFilePath) } } }) } } }) } // 注册音频控件 registerAudioContext(path) { this.innerAudioContext.src = path this.innerAudioContext.play() // 避开 IOS 端静音状态没法播放的问题 this.innerAudioContext.obeyMuteSwitch = false this.innerAudioContext.onEnded(res => { // isPlaying 记录是否在播放中 this.setState({ isPlaying: false }) this.innerAudioContext.stop() }) this.innerAudioContext.onError(res => { // 播放音频失败的回调 }) this.innerAudioContext.onPlay(res => { // 开始播放音频的回调 }) this.innerAudioContext.onStop(res => { // 播放音频停止的回调 }) }

这里做了两件事情:

用 wx.downloadFile() 接口将文件下载下来,注意参数中 header 属性, Content-type 值为 audio/mp3。即将此文件识别为音频类文件。这里用到微信里的文件管理器 wx.getFileSystemManager() ,接口中的 saveFile() 方法可以把文件保存到本地 用 wx.createInnerAudioContext() 的 play() 方法播放存在本地的音乐 mp3 文件 3.2 性能优化

这里考虑到播放完之后,存在手机的录音文件会越来越多。我们想想办法,做一做性能优化工作。也就是在恰当的时机清除多余文件。

代码如下:

componentWillUnmount() { this.clearDir() } // 删除下载的音频文件 clearDir() { const FileSystemManager = wx.getFileSystemManager() const __dirPath = wx.env.USER_DATA_PATH FileSystemManager.readdir({ dirPath: __dirPath, success: res => { const { errMsg, files } = res if (errMsg == 'readdir:ok') { files.forEach(item => { FileSystemManager.unlink({ filePath: `${__dirPath}/${item}` }) }) } } }) }

梳理一下:

用 wx.getFileSystemManager() 接口中 readdir() 方法读取到指定目录(wx.env.USER_DATA_PATH)的所有文件。在其读取成功的回调中做一个 forEach 循环,然后用 unlink() 删除文件。最后将此方法放在生命周期 componentWillUnmount 中调用。

四、PC 端音频播放

小程序的录音和播放都简单的介绍了,这里也拓展一下。说一说 PC 端比较原始的音频播放方法。

项目中没有引用播放器插件,这里直接用 audio 标签来实现。 html 的部分如下:

const { voice_url, isPlaying } = this.state; return ( 音频: {isPlaying ? '停止' : '播放'} {}} onTimeUpdate={() => {}}> )

然后看下 PC 端解析播放部分,和小程序原理差不多,先下载,后播放。代码如下:

// 播放或者暂停 onBtnClick = () => { const { isPlaying } = this.state; // 区分播放还是暂停 if (isPlaying) { this.audioRef.current.pause(); } else { this.downloadFile(); } this.setState({ isPlaying: !isPlaying }); }; // 下载文件 downloadFile = () => { const { download_url } = this.state; axios.get(download_url as string, { responseType: 'blob' }).then((res: any) => { const reader = new FileReader(); const data = res.data; reader.onload = e => { this.executeDownload(data); }; reader.readAsText(data); }); }; // 在浏览器上预览音频文件 executeDownload = (data: any) => { if (!data) { return; } // 将文件转化音频流的链接 const url = window.URL.createObjectURL(new Blob([data], { type: 'audio/mp3' })); // 前端存储这个链接 this.setState({ voice_url: url }); };

梳理:

创建 audio 标签作为音频播放的容器 点击页面的播放按钮触发文件下载方法 通过 axios 下载资源文件,用 new FileReader() 读取文件,并且在文件完全加载时,利用 window.URL.createObjectURL() 方法生成可以在浏览器上预览音频文件的链接 audio 监听到 src 属性的变化时,会自动播放出声音 五、感谢 如果本文对你有帮助,就点个赞支持下吧!感谢阅读。 文中还有很多不完善的地方,欢迎大家在评论区提出疑问。


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3