title: 个人语音实现
date: 2015-11-28 16:09:53
categories: "编程"
tags: [语音, 七牛, 微信JS-SDK, 高校之恋]
自己挖的坑,果然要自己填!
由于之前开启了CRSF,想增强安全性。过了一段时间,自己吧这事给忘了,导致JS的ajax消息无法发送到后台,一直提示400 BAD request。找了好久才发现,详情。
但是也找到个可以检测并且建议的办法,ajax提供error反馈错误信息。
$.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上传数据,相当简单。
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上有七牛程序员入驻,可查到很多东西
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即可。
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兼容性(如果最早就加入并进行检测,前面的都不用折腾)
// 检测能否兼容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的数据,即可知道是否兼容。