最近开发的微信项目里面需要用到微信语音的功能,结合微信开发文档的网页js接口,语音可以在网页上生成并上传到微信服务器,但是微信服务器保存的时间有限,还是保存到自己的本地服务器比较稳当,这样需要把语音文件的serverId传到后台,在后台调用微信api获取语音文件,但获取的音频文件是amr格式的,但是前端的html5的“audio”标签只支持MP3、wav、ogg三种格式,这样又需要将amr文件转为MP3文件,ffmpeg是一般的音视频文件格式转化框架。
总结一下微信语音保存展示的整体流程:
1.调用微信js,生成语音文件
2.上传语音到微信服务器
3.调用微信资源文件接口获取语音文件保存到本地服务器
4.将保存的语音文件由amr转mp3
5.用html5的audio标签播放mp3文件
前端jsp页面如下,
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <meta name="viewport" content="width=640, user-scalable=no"> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Expires" content="0"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="format-detection" content="telephone=no"> <meta name="apple-mobile-web-app-title" content=""> <title>录音test</title> <style type="text/css"> *{ margin:0px; padding:0px; box-sizing:border-box; -webkit-tap-highlight-color:rgba(0,0,0,0);} html{ max-width:640px; margin:0 auto;} body{ font-family:"PingFangSC-Regular","sans-serif","STHeitiSC-Light","微软雅黑","Microsoft YaHei"; font-size:24px; line-height:1.5em; color:#000; -webkit-user-select:none; user-select:none; -webkit-touch-callout:none; touch-callout:none; } .start_btn , .play_btn , .send_btn{ width:250px; height:60px; line-height:60px; margin:20px auto; text-align:center; border:#eee solid 2px; cursor:pointer;} .start_btn_in , .stop_btn{ color:#f00; border:#f00 solid 2px;} </style> </head> <body> <div class="start_btn">点我录音</div> <div class="play_btn">点我播放</div> <div class="send_btn">点我保存</div> <audio id="au" src="" controls="controls"></audio> <script src="<%=path%>/js/jquery-1.7.1.js" type="text/javascript"></script> <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <script type="text/javascript"> wx.config({ debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: '${appId}', // 必填,公众号的唯一标识 timestamp:${timestamp}, // 必填,生成签名的时间戳 nonceStr: '${nonceStr}', // 必填,生成签名的随机串 signature: '${signTrue}',// 必填,签名,见附录1 jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage','startRecord','stopRecord','onVoiceRecordEnd','playVoice','stopVoice','onVoicePlayEnd','uploadVoice'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //返回音频的本地ID var localId; //返回音频的服务器端ID var serverId; //录音计时,小于指定秒数(minTime = 10)则设置用户未录音 var startTime , endTime , minTime = 1; //***********************************// //开始录音 $('.start_btn').on('touchstart',function(e){ e.preventDefault(); var $this = $(this); $this.addClass('start_btn_in'); startTime = new Date().getTime(); //开始录音 wx.startRecord(); }); //***********************************// //停止录音接口 $('.start_btn').on('touchend', function(){ var $this = $(this); $this.removeClass('start_btn_in'); //停止录音接口 wx.stopRecord({ success: function (res) { localId = res.localId; } }); endTime = new Date().getTime(); alert((endTime - startTime) / 1000); if((endTime - startTime) / 1000 < minTime){ localId = ''; alert('录音少于' + minTime + '秒,录音失败,请重新录音'); } }); //监听录音自动停止接口 wx.onVoiceRecordEnd({ //录音时间超过一分钟没有停止的时候会执行 complete 回调 complete: function (res) { localId = res.localId; $('.start_btn').removeClass('start_btn_in'); } }); //***********************************// $('.play_btn').on('click',function(){ if(!localId){ alert('您还未录音,请录音后再点击播放'); return; } var $this = $(this); if($this.hasClass('stop_btn')){ $(this).removeClass('stop_btn').text('点我播放'); //停止播放接口 wx.stopVoice({ //需要停止的音频的本地ID,由 stopRecord 或 onVoiceRecordEnd 接口获得 localId: localId }); }else{ $this.addClass('stop_btn').text('点我停止'); //播放语音接口 wx.playVoice({ //需要播放的音频的本地ID,由 stopRecord 或 onVoiceRecordEnd 接口获得 localId: localId }); } }); //监听语音播放完毕接口 wx.onVoicePlayEnd({ //需要下载的音频的服务器端ID,由uploadVoice接口获得 serverId: localId, success: function (res) { $('.play_btn').removeClass('stop_btn').text('点我播放'); } }); //***********************************// //上传语音接口 $('.send_btn').on('click',function(){ if(!localId){ alert('您还未录音,请录音后再保存'); return; } //上传语音接口 wx.uploadVoice({ //需要上传的音频的本地ID,由 stopRecord 或 onVoiceRecordEnd 接口获得 localId: localId, //默认为1,显示进度提示 isShowProgressTips: 1, success: function (res) { //返回音频的服务器端ID serverId = res.serverId; $.ajax({ async:true,//是否异步 type: "post", cache:false, dataType: "text", data:{'mediaId':serverId}, url: "<%=path%>/upload/downloadVoice.do", success: function(data) { var ret = $.parseJSON(data); if (ret.statusCode == '200') { var filePath = ret.message; $("#au").attr("src",filePath); alert('已获取上传的语音文件!'); } else if (ret.statusCode == '300') { alert('系统故障!'); } }, error: function(data){ } }); } }); }); }); </script> </body> </html>
保存语音文件如下:
public String downloadVoice(){ try { if (downloadVoiceFromWx()) { ajaxDone = new AjaxDone(CommonEnum.STATUS_CODE_OK.getDescription(), "下载成功!"); ajaxDone.setNavTabId(ssid); ajaxDone.setMessage(pathName); writerJson(ajaxDone); return null; } else { log.error("获取微信语音失败"); } ajaxDone = new AjaxDone(CommonEnum.STATUS_CODE_ERROR.getDescription(), "下载失败!"); }catch (Exception e){ log.error(e +"微信语音下载失败"); } writerJson(ajaxDone); return null; } public boolean downloadVoiceFromWx(){ try { AccessTokenUtil accessTokenUtil = new AccessTokenUtil(); String accessToken = accessTokenUtil.getAccessToken(); log.info("微信accessTokenid1:" + accessToken); log.info("mediaId:" + mediaId); if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(mediaId)) return false; String requestUrl = ""; requestUrl = download_media_url.replace("ACCESS_TOKEN", accessToken).replace("MEDIA_ID", mediaId); log.info("获取文件的微信请求>>" + requestUrl); String vFileName = getResourceFile(requestUrl); String newVFileName = ""; String extName = vFileName.substring(vFileName.lastIndexOf(".")); UploadFileTypeEnum uploadFileTypeEnum = UploadFileTypeEnum.checkByExt(extName); newVFileName = vFileName.substring(0,vFileName.lastIndexOf(".")) + ".mp3"; String fileDire = getFileDirectoryPath(); amrToMp3(fileDire + vFileName,fileDire + newVFileName);
//保存文件信息到数据库 -- 自己实现 log.info("微信音频下载路径:"+ pathName); return true; }catch (Exception e){ e.printStackTrace(); log.error("获取音频文件失败" + mediaId); return false; } } public String getFileDirectoryPath(){ String bashPath2 = null; //访问的url中间目录=磁盘上保存的中间目录 String bashPath = Config.getConfig().get(Config.FILE_UPLOAD_PATH); //磁盘上保存基础路径 bashPath2 = Config.getConfig().get(Config.FILE_UPLOAD_URL); String savePath = bashPath + bashPath2; //保存路径 File file = new File(savePath); if (!file.exists()) { file.mkdirs(); } return savePath; } public String getResourceFile(String url){ String vFileName = ""; try { URL urlGet = new URL(url); HttpURLConnection http = (HttpURLConnection) urlGet .openConnection(); http.setRequestMethod("GET"); http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); http.setDoOutput(true); http.setDoInput(true); http.connect(); // 获取文件转化为byte流 InputStream inputStream = http.getInputStream(); //获取文件名 String ContentDisposition = ""; ContentDisposition = http.getHeaderField("Content-disposition"); vFileName = ContentDisposition.substring(ContentDisposition.indexOf("filename")+10, ContentDisposition.length() -1); String vFilePath = getFileDirectoryPath() + vFileName; //写入音频新文件 FileOutputStream fileOutputStream = null; int size=0; size = inputStream.available(); byte[] data = new byte[size]; int len = 0; fileOutputStream = new FileOutputStream(vFilePath); while ((len = inputStream.read(data)) != -1) { fileOutputStream.write(data, 0, len); } log.info("生成微信音频文件,文件为:" + vFilePath); inputStream.close(); fileOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } return vFileName; }
amr转mp3需要调用ffmpeg,下载站点:https://johnvansickle.com/ffmpeg/ 或 http://ffmpeg.org/download.html
解压文件找到ffmpeg文件,放到linux服务器目录下,然后通过java代码调用linux命令行执行转化,代码如下:
public boolean amrToMp3(String localPath, String targetFilePath) { try { log.info("************** ffmpeg ****************"); java.lang.Runtime rt = Runtime.getRuntime(); String command = "/home/user/server/apache-tomcat/temp/jave-1/ffmpeg -i " + localPath + " " + targetFilePath; log.info("command = " + command); Process proc = rt.exec(command); InputStream stderr = proc.getErrorStream(); InputStreamReader isr = new InputStreamReader(stderr); BufferedReader br = new BufferedReader(isr); String line = null; StringBuffer sb = new StringBuffer(); while ((line = br.readLine()) != null){ sb.append(line); } log.info("ffmpeg Process errorInfo: " + sb.toString()); int exitVal = proc.waitFor(); log.info("ffmpeg Process exitValue: " + exitVal); return true; } catch (Exception e) { log.error("ffmpeg exec cmd Exception " + e.toString()); } return false; }
获取mp3文件后,audio控件直接引用linux下的该文件即可播放。