个人语音实现

此处输入图片的描述

自己挖的坑,果然要自己填!

由于之前开启了CRSF,想增强安全性。过了一段时间,自己吧这事给忘了,导致JS的ajax消息无法发送到后台,一直提示400 BAD request。找了好久才发现,详情

但是也找到个可以检测并且建议的办法,ajax提供error反馈错误信息。

1
2
3
4
5
6
7
8
9
10
11
$.ajax({
url: "/profile/{{ user_id }}",
type: 'POST',
data: {data: "failed"},
error: function(e) { //增加错误反馈
console.log(e);
}
})
.done(function (data) {
console.log(data);
});

前后端交互

  • 访问个人主页(wx_config)
  • 微信上传语音
  • 传递mediaId和record_time到后台
  • 获取token从微信下载语音
  • 上传语音到qiniu(发起任务转换amr为mp3)
  • 存储url+time到数据库

下载语音

获取token和ticket

说明一下:两处需要用到access_token,一是在微信账号绑定的域名下使用JS-SDK上传文件时,通过token来生成ticket来初始化JS-SDK;二是在下载文件时需要认证号产生的token。

本以为JS-SDK的可以使用未认证账号来产生access_token从而产生ticket用于JS端上传数据,从而避免出现提示“XXXX”需要使用录音功能。但是下载时发现,微信会校验mediaId是否来自同一个公众号(不校验才怪勒)。所以上传和下载必须使用同一个已认证微信公众号的APPID和APPSECRET来产生token。

两个不同的服务器都需要token和ticket,为了避免抢走导致一方失效。一个服务器产生token和ticket存于缓存中,每小时更新一次(微信限制每天不超过2000次),并提供其他服务器获取token和ticket的接口。其他服务器需要使用直接通过接口获取即可。

录音时间

录音时间不需要单独存储,直接存储在audio的链接中,通过锚点的方式
http://7xogxw.com1.z0.glb.clouddn.com/2015-11-26 23:11:06.709113#time23
每次读取audio_url,先分割获取录音时间,url和time分别传到前端渲染。

真正的自适应:关于图片,以后可以通过类似的方式,用锚点记录图片的实际长宽,而在前端加载之前,通过url即可获取长宽数据,从而根据设备实际分辨率,折算合适长宽并发起请求,减少大图加载缓慢。同时可以在图片加载之前就可以生成固定比例的图片占位符,避免加载过程中出现页面跳动。

上传语音

上传文件

之前已经实现过七牛图片的上传,但是当初的接口复杂难用(@Lee 佩服),想着要修改之前的上传接口来上传语音文件,我决定还是使用最新qiniu-sdk来上传文件,之前图床是用到过,使用简洁了很多。

但是一个麻烦的问题,新版本的sdk向下兼容性不是很好,如果使用最新的,之前的会报错,具体不知。而要去修改之前的接口和上传部分代码,工作量太大,于是两个版本sdk同时使用,导入新的包为qiniu2,修改所有引用qiniu位置为qiniu2,OK,一切正常。

一般情况,直接获取token上传数据,相当简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import qiniu2
from datetime import datetime
import config
from qiniu2 import BucketManager
import base64
def get_token():
q = qiniu.Auth(config.QINIU_ACCESS_KEY, config.QINIU_SECRET_KEY)
token = q.upload_token(config.PIC_BUCKET)
return token
up_token, key = upload.get_token()
ret, info = qiniu2.put_data(up_token, key, resp)
assert ret['key'] == key

格式转换

而微信默认amr格式,html不支持。上传语音需要预处理,需要policy用于存储转换的格式、另存到位置和名称等信息来产生up_token。并且如果policy和put_data中的key相同且bucket相同,即可同名覆盖,减少空间使用。

这一段单独看七牛的文档你是啥也看不到的,只告诉你预处理格式。segmentfault上有七牛程序员入驻,可查到很多东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def get_token():
q = qiniu2.Auth(config.QN_ACCESS_KEY, config.QN_SECRET_KEY)
# 文件名称
key = str(datetime.now())
# 转换格式
format = "avthumb/ogg"
# 另存到bucket和对应名称key
entry_uri = config.AUDIO_BUCKET + ':' + key
entry_uri = "saveas/"+ base64.urlsafe_b64encode(entry_uri)
# 独立队列audioQueue转换
policy = {
"persistentOps": format + "|" + entry_uri,
"persistentPipeline": "audioQueue"
}
token = q.upload_token(config.AUDIO_BUCKET, policy=policy)
return token, key
up_token, key = upload.get_token()
ret, info = qiniu2.put_data(up_token, key, resp)
assert ret['key'] == key

删除文件

上传新的语音的时候,需要删除原来的语音文件。同样很简单,提供需要删除的问题件的key即可。

1
2
3
4
5
def del_file(upkey):
q = qiniu2.Auth(config.QN_ACCESS_KEY, config.QN_SECRET_KEY)
bucket = BucketManager(q)
ret, info = bucket.delete(config.AUDIO_BUCKET, upkey)
return ret, info

语音播放

兼容格式

一开始看html的audio标签的时候,默认ogg和mp3均可。所以默认转换为ogg。最后iOS上不无法播放,于是检查发现iOS压根就不兼容ogg格式,最终转换为mp3格式。

  • iOS录音PC能播放,不是前端、后台原因。
  • 确定iOS的微信兼容audio
    全民K哥能播放,全民K哥的js判断支持h5则使用audio否则flash,而微信支持h5。通过canPlayType属性即可检测。
  • 测试audio兼容性(如果最早就加入并进行检测,前面的都不用折腾)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 检测能否兼容audio
    function support_audio(){
    return !!document.createElement('audio').canPlayType;
    }
    // 检测能否兼容ogg
    function support_audio_ogg(){
    var elem = document.createElement('audio');
    return elem.canPlayType('audio/ogg; codecs="vorbis"');
    }
    // 检测能否兼容mp3
    function support_audio_mp3(){
    var elem = document.createElement('audio');
    return elem.canPlayType('audio/mpeg;');
    }
    console.log('audio:' + support_audio());
    console.log('audio-ogg:'+ support_audio_ogg());
    console.log('audio-mp3:'+ support_audio_mp3());

参考:http://www.vnadd.com/25160.htm

  • 使用jsconsole远程调试
    通过该工具可以远程查看访问机器输出到console的数据,即可知道是否兼容。

原文链接