微信小程序之上传录音功能详述及前后端代码分享
主要功能:获取服务器端作业,循环输出作业内容,为每条作业实现录音上传
大坑:点击循环列表中的一个按钮,其他按钮也一起激活。。。找了好多资料。综合各个资料。终于解决这个问题(点击组件,获取点击下标。给每个组件赋值为当前下标,判断点击的下标是否等于当前组件的小标,从而判断点击的哪个组件)
先上大家最关心的js代码:
// pages/audio/audio.js
var common = require('../../utils/util.js'); //获取时间
const app = getApp()
const innerAudioContext = wx.createInnerAudioContext();
Page({
data: {
isPlayAudio: false, //暂停播放按钮显示切换
chooseday: "", //选择日期(通过setData默认为今天)
choosekemu: "", //选择科目 (通过setData默认为语文)
kemu: ['语文作业', '数学作业', '英语作业', '其他作业'], //科目列表
hw_audio_area: [], //需要循环的作业内容
hw_audio_array: [], //需要循环的临时数组
index: '', //点击循环数组中元素的下标,表示当前点击了哪个元素
checkid: -1, //点击“开始录音”按钮的下标。设为-1表示默认未点击
checkidEnd: -1, //点击“结束录音”按钮的下标。设为-1表示默认未点击
checkidre: -1, //点击“重新录音”按钮的下标。设为-1表示默认未点击
name: '', //用户名
serverPath: '', //服务器端本地路径
httpPath: '', //服务器端http路径
/*播放器*/
audiolist: [{
audiosrc: '',
}],
isPlayAudio: false,
audioSeek: 0,
audioDuration: 0,
showTime1: '00:00',
showTime2: '00:00',
audioTime: 0
},
onLoad: function() {
//获取系统日期及用户名
var today = common.formatTime(new Date()); //获取系统日期,今天
const name = getApp().globalData.name; //获取全局变量,登录用户名
this.setData({
name
})
this.setData({
chooseday: today, //设置默认的日期
choosekemu: '语文作业', //设置默认的科目
})
this.hw_request() //服务器请求函数
},
/*选择科目 */
kemuChange: function(e) {
this.setData({
chooseday, //重新设置系统日期为选中的日期
choosekemu: this.data.kemu[e.detail.value]
})
this.hw_request()
},
/*选择日期 */
bindDateChange: function(e) {
this.setData({
chooseday: e.detail.value,
choosekemu //重新设置科目为选中的科目
})
this.hw_request()
},
// 查询作业服务器请求函数
hw_request() {
var that = this
wx.request({
url: 'https://www.ailaiyun.com/1.php', //服务器地址
method: 'post',
data: {
type: 'hw_get_audio',
hw_kemu: that.data.choosekemu,
today: that.data.chooseday,
},
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
success(res) {
// console.log(res.data)
//如果服务器返回的data值为空表示当天这个科目下没有作业
if (res.data == null) {
that.setData({
hw_audio_area: ''
})
wx.showToast({
title: '今日暂无' + that.data.choosekemu + '(录音)',
icon: 'none'
})
} else {
var hw_audio_array = that.data.hw_audio_array; // 根据服务器返回的数组组合为新的数组
var hw_audio_area = that.data.hw_audio_area; // 服务器返回的数组
for (let i = 0; i < res.data.length; i++) {
hw_audio_array.push(res.data[i].hw_audio) //提取服务器返回的数组中的需要的对象
that.setData({
hw_audio_array
})
// console.log(hw_audio_array)
}
that.removeEmpty(hw_audio_array) //去除数组中的空值
var str = String(hw_audio_array); //转换为字符串
var arr = str.split("^"); //去除旧字符串中的'^'并赋值一个新的字符串
that.removeEmpty(arr) //再次清除数组中的空值
//把值存到需要要循环的数组中
that.setData({
hw_audio_area: arr
})
// console.log(that.data.hw_audio_area)
}
},
})
},
/**去除数组中的空值 */
removeEmpty: function(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] == "" || typeof(arr[i]) == "undefined" || arr[i] == ",") {
arr.splice(i, 1);
i = i - 1; // i - 1 ,因为空元素在数组下标 2 位置,删除空之后,后面的元素要向前补位
}
}
return arr;
},
/*开始录音 */
start: function(e) {
// console.log(e)
// console.log(e.currentTarget.dataset.id)
var checkid = e.currentTarget.dataset.index //点击开始的按钮的下标
//控制结束按钮显示
this.setData({
checkid: checkid,
checkidre: checkid,
checkidEnd: -1
})
// console.log(this.data.checkid)
//启动录音功能
const recorderManager = wx.getRecorderManager()
recorderManager.onStart(() => {
console.log('recorder start')
})
recorderManager.onStop((res) => {
console.log('recorder stop', res)
const {
tempFilePath
} = res
})
recorderManager.onFrameRecorded((res) => {
const {
frameBuffer
} = res
console.log('frameBuffer.byteLength', frameBuffer.byteLength)
})
const options = {
duration: 600000,
sampleRate: 8000,
numberOfChannels: 1,
encodeBitRate: 48000,
format: 'mp3',
frameSize: 50
}
recorderManager.start(options)
},
/**结束录音 */
stop: function(e) {
var checkidEnd = e.currentTarget.dataset.index //点击结束录音按钮传出的下标
//是最后一组上传控件显示出来
this.setData({
checkidEnd: checkidEnd,
checkid: -1
})
//结束录音功能
const recorderManager = wx.getRecorderManager()
recorderManager.onStop((res) => {
// console.log(res.tempFilePath)
this.setData({
'audiolist[0].audiosrc': res.tempFilePath
})
})
recorderManager.onFrameRecorded((res) => {
const {
frameBuffer
} = res
console.log('frameBuffer.byteLength', frameBuffer.byteLength)
})
const options = {
duration: 10000,
sampleRate: 44100,
numberOfChannels: 1,
encodeBitRate: 192000,
format: 'aac',
frameSize: 50
}
recorderManager.stop(options)
},
/**重新录制 */
rere: function(e) {
//控制“开始录音”按钮重新显示出来
var checkidre = e.currentTarget.dataset.index
this.setData({
checkidre: -1,
checkidEnd: -1
})
},
/*上传录音 */
upload: function() {
var user_id = this.data.name //需要上传的用户名
var pathAudio = this.data.audiolist[0].audiosrc //需要上传的录音文件路径
var checkidEnd = this.data.checkidEnd //根据点击的ID判断上传那一条录音作业信息
var hw = this.data.hw_audio_area[checkidEnd]
var that = this
wx.showLoading({
title: '录音上传中',
mask: true
})
wx.uploadFile({
url: 'https://www.ailaiyun.com/2.php',
filePath: pathAudio,
name: 'file',
formData: {
user_id: user_id
},
success(res) {
var data = res.data
var obj = JSON.parse(data) //把服务器返回的值转换成json格式
// console.log(obj)
var httpPath = obj.httpPath
var serverPath = obj.serverPath
that.setData({
httpPath,
serverPath
})
// console.log(that.data.serverPath)
//把作业信息等数据上传到数据库
if (res.statusCode == "200") {
wx.request({
url: 'https://www.ailaiyun.com/3.php',
method: "post",
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: {
type: "addAudioPath",
name: user_id,
httpPath: that.data.httpPath,
serverPath: that.data.serverPath,
date: that.data.chooseday,
kemu: that.data.choosekemu,
hw: hw
},
success(res) {
wx.hideLoading()
console.log(res.data)
},
})
wx.showToast({
title: '录音上传成功',
icon: 'success',
})
}
}
})
},
/**播放器 */
onShow: function(e) {
// console.log(e)
this.Initialization();
this.loadaudio();
},
//初始化播放器,获取duration
Initialization() {
var t = this;
if (this.data.audiolist[0].audiosrc.length != 0) {
//设置src
innerAudioContext.src = this.data.audiolist[0].audiosrc;
//运行一次
innerAudioContext.play();
innerAudioContext.pause();
innerAudioContext.onCanplay(() => {
//初始化duration
innerAudioContext.duration
setTimeout(function() {
//延时获取音频真正的duration
var duration = innerAudioContext.duration;
var min = parseInt(duration / 60);
var sec = parseInt(duration % 60);
if (min.toString().length == 1) {
min = `0${min}`;
}
if (sec.toString().length == 1) {
sec = `0${sec}`;
}
t.setData({
audioDuration: innerAudioContext.duration,
showTime2: `${min}:${sec}`
});
}, 1000)
})
}
},
//拖动进度条事件
sliderChange(e) {
var that = this;
innerAudioContext.src = this.data.audiolist[0].audiosrc;
//获取进度条百分比
var value = e.detail.value;
this.setData({
audioTime: value
});
var duration = this.data.audioDuration;
//根据进度条百分比及歌曲总时间,计算拖动位置的时间
value = parseInt(value * duration / 100);
//更改状态
this.setData({
audioSeek: value,
isPlayAudio: true
});
//调用seek方法跳转歌曲时间
innerAudioContext.seek(value);
//播放歌曲
innerAudioContext.play();
},
//播放、暂停按钮
playAudio() {
//获取播放状态和当前播放时间
var isPlayAudio = this.data.isPlayAudio;
var seek = this.data.audioSeek;
innerAudioContext.pause();
//更改播放状态
this.setData({
isPlayAudio: !isPlayAudio
})
if (isPlayAudio) {
//如果在播放则记录播放的时间seek,暂停
this.setData({
audioSeek: innerAudioContext.currentTime
});
} else {
//如果在暂停,获取播放时间并继续播放
innerAudioContext.src = this.data.audiolist[0].audiosrc;
if (innerAudioContext.duration != 0) {
this.setData({
audioDuration: innerAudioContext.duration
});
}
//跳转到指定时间播放
innerAudioContext.seek(seek);
innerAudioContext.play();
}
},
loadaudio() {
var that = this;
//设置一个计步器
this.data.durationIntval = setInterval(function() {
//当歌曲在播放时执行
if (that.data.isPlayAudio == true) {
//获取歌曲的播放时间,进度百分比
var seek = that.data.audioSeek;
var duration = innerAudioContext.duration;
var time = that.data.audioTime;
time = parseInt(100 * seek / duration);
//当歌曲在播放时,每隔一秒歌曲播放时间+1,并计算分钟数与秒数
var min = parseInt((seek + 1) / 60);
var sec = parseInt((seek + 1) % 60);
//填充字符串,使3:1这种呈现出 03:01 的样式
if (min.toString().length == 1) {
min = `0${min}`;
}
if (sec.toString().length == 1) {
sec = `0${sec}`;
}
var min1 = parseInt(duration / 60);
var sec1 = parseInt(duration % 60);
if (min1.toString().length == 1) {
min1 = `0${min1}`;
}
if (sec1.toString().length == 1) {
sec1 = `0${sec1}`;
}
//当进度条完成,停止播放,并重设播放时间和进度条
if (time >= 100) {
innerAudioContext.stop();
that.setData({
audioSeek: 0,
audioTime: 0,
audioDuration: duration,
isPlayAudio: false,
showTime1: `00:00`
});
return false;
}
//正常播放,更改进度信息,更改播放时间信息
that.setData({
audioSeek: seek + 1,
audioTime: time,
audioDuration: duration,
showTime1: `${min}:${sec}`,
showTime2: `${min1}:${sec1}`
});
}
}, 1000);
},
onUnload: function() {
//卸载页面,清除计步器
clearInterval(this.data.durationIntval);
}
})
wxml代码:
<!--pages/audio/audio.wxml-->
<view class='choose'>
<picker mode="selector" range='{{kemu}}' bindchange='kemuChange'>
<view class='title-ch'>选择科目:{{choosekemu}}</view>
</picker>
<picker mode="date" value="{{date}}" start="2019-04-01" end="2029-01-01" bindchange="bindDateChange" value='{{today}}'>
<view class='now-date'>日期:{{chooseday}}</view>
</picker>
</view>
<view class='hw'>
<view class='hw-title'>录音作业</view>
</view>
<scroll-view class='area-content' scroll-y='true'>
<view wx:for="{{hw_audio_area}}" class="hw_audio_list" wx:key="{{id}}">
<text class='content'>{{item}}</text>
<!-- 判断点击的哪个组件 -->
<block wx:if="{{checkidre == index}}">
</block>
<block wx:else>
<view catchtap='start' data-index='{{index}}' class='btn' id='{{index}}'>开始录音</view>
</block>
<!-- 判断点击的哪个组件 -->
<block wx:if="{{checkid == index}}">
<view catchtap='stop' data-index='{{index}}' class='btn-record' id='{{index}}'><text style='font-size:34rpx'>正在录音</text><text style='color:red;font-weight:bold'>结束录音</text></view>
</block>
<block wx:else>
</block>
<!-- 判断点击的哪个组件 -->
<view wx:if='{{checkidEnd == index}}'>
<view class='last_list'>
<view catchtap='playAudio' data-id='{{index}}' class='play_the_audio' id='{{index}}'>
<text wx:if='{{isPlayAudio}}'>暂停</text>
<text wx:else>播放</text>
</view>
<view bindtap='upload' class='upload'>上传</view>
<view catchtap='rere' data-index='{{index}}' class='rere' id='{{index}}'>重录</view>
</view>
<view class='audioControls'>
<view class='flex'>
<view class='bottom' catchtap='playAudio' data-id='{{index}}'>
<!-- 按钮 -->
<view wx:if="{{isPlayAudio}}">
<image src='../images/pause.png' />
</view>
<view wx:else>
<image src='../images/play.png' />
</view>
</view>
<view class='slider'>
<slider bindchange='sliderChange' activeColor='red' block-size="12" value='{{audioTime}}' data-id='{{index}}' />
</view>
<view class='time'>
{{showTime1}}/{{showTime2}}
</view>
</view>
</view>
</view>
</view>
</scroll-view>
wxss代码:
/* pages/audio/audio.wxss */
page {
background-image: url("https://www.ailaiyun.com/xxxxx.jpg");
}
.choose {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-around;
height: 100rpx;
margin-top: 30rpx;
}
.title-ch, .now-date {
background-color: #14a1fd;
font-size: 30rpx;
text-align: center;
height: 50rpx;
line-height: 40rpx;
border: 1rpx solid #14a1fd;
width: 320rpx;
border-radius: 15rpx;
}
.hw {
display: flex;
justify-content: center;
}
.hw-title{
font-weight: bold;
}
.area-content {
display: flex;
width: 95%;
justify-content: center;
flex-direction: column;
margin: 0 auto;
height: 950rpx;
}
.hw_audio_list {
margin-top: 20rpx;
width: 98%;
display: flex;
justify-content: center;
flex-direction: column;
border: 5rpx solid #14a1fd;
border-radius: 15rpx;
}
.content {
color: blue;
margin: 0 auto;
}
.btn {
margin: 10rpx auto;
background-color: #14a1fd;
width: 200rpx;
height: 50rpx;
border: 1rpx solid #14a1fd;
border-radius: 15rpx;
text-align: center;
}
.btn-record{
margin: 10rpx auto;
background-color: #14a1fd;
width: 160rpx;
height: 100rpx;
border: 1rpx solid #14a1fd;
border-radius: 15rpx;
text-align: center;
}
.last_list {
display: flex;
flex-direction: row;
justify-content: center;
align-items:center;
}
.play_the_audio {
margin: 10rpx auto;
background-color:green;
width: 140rpx;
height: 50rpx;
border: 1rpx solid green;
border-radius: 15rpx;
text-align: center;
}
.rere {
margin: 10rpx auto;
background-color: orange;
width: 140rpx;
height: 50rpx;
border: 1rpx solid orange;
border-radius: 15rpx;
text-align: center;
}
.upload-content {
width: 100%;
position: fixed;
bottom: 50rpx;
display: flex;
justify-content: center;
margin: 0 auto;
}
.upload {
width: 100rpx;
background: #14a1fd;
color: #fff;
border-radius: 50%;
height: 100rpx;
text-align: center;
line-height: 100rpx;
}
/*音频播放器*/
.flex {
display: flex;
}
.audioControls {
margin: 20rpx auto;
width: 100%;
height: 80rpx;
background: green;
opacity: 0.8;
bottom: 0;
color: white;
font-size: 6pt;
line-height: 80rpx;
text-align: center;
}
.audioControls .bottom {
width: 60rpx;
height: 100%;
}
.audioControls .bottom image {
margin-top: 30%;
margin-left: 30%;
width: 40rpx;
height: 40rpx;
}
.audioControls .slider {
width: 520rpx;
height: 100%;
}
.slider slider {
width: 95%;
margin-left: 4%;
margin-right: 0;
}
.audioControls .time {
width: 120rpx;
height: 100%;
}
PHP后端代码
1.php
//学生查询音频作业
elseif ($type=="hw_get_audio") {
$post_kemu =$_POST['hw_kemu'];
$post_today =$_POST['today'];
$con = mysqli_connect("localhost","xxxxx","xxxxx") or die("数据库连接失败");//连接数据库
mysqli_query($con,"set names utf8");
mysqli_select_db($con,"stu") or die("数据库选择失败");
$sql_search="SELECT hw_audio from hw_text where hw_kemu = '$post_kemu' and hw_date = '$post_today'";
$result_search = mysqli_query($con,$sql_search);
while ($row = mysqli_fetch_array($result_search)) {
$res[] = $row;
}
echo json_encode($res);
}
2.php
<?php
date_default_timezone_set("Asia/Shanghai"); //设置时区
$code = $_FILES['file'];//获取小程序传来的图片
if(is_uploaded_file($_FILES['file']['tmp_name'])) {
//把文件转存到你希望的目录(不要使用copy函数)
$uploaded_file=$_FILES['file']['tmp_name'];
$username = $_POST["user_id"];
//我们给每个用户动态的创建一个文件夹
$user_path=$_SERVER['DOCUMENT_ROOT']."uploadaudio/".$username;
//判断该用户文件夹是否已经有这个文件夹
if(!file_exists($user_path)) {
mkdir($user_path);
}
$file_true_name=$_FILES['file']['name'];
$random = uniqid();
$move_to_file=$user_path."/".date("Y-m-d")."-".time().$random.substr($file_true_name,strrpos($file_true_name,"."));
if(move_uploaded_file($uploaded_file,iconv("utf-8","gb2312",$move_to_file))) {
$arr=array('httpPath' => "https://".$_SERVER['SERVER_NAME']."/uploadaudio/".$username."/".date("Y-m-d")."-".time().$random.substr($file_true_name,strrpos($file_true_name,".")),'serverPath' => $move_to_file);
echo json_encode($arr,JSON_UNESCAPED_SLASHES);
}
}
?>
3.php
//上传录音作业
elseif($type == "addAudioPath"){
function res(){
$post_name = $_POST["name"];
$post_date = $_POST["date"];
$post_kemu = $_POST["kemu"];
$post_serverPath = $_POST["serverPath"];
$post_httpPath = $_POST["httpPath"];
$post_hw = $_POST["hw"];
$con = mysqli_connect("localhost","xxxx","xxxx") or die("数据库连接失败");//连接数据库
mysqli_query($con,"set names utf8");
mysqli_select_db($con,"stu") or die("数据库选择失败");
$sql_insert="INSERT INTO hw_audio (id,name,post_date,kemu,serverPath,httpPath,hw) VALUES (null,'$post_name','$post_date','$post_kemu','$post_serverPath','$post_httpPath','$post_hw')";
$result_insert = mysqli_query($con,$sql_insert);
echo "数据库上传成功";
}
res();
}