【记录】微信录音开发(企业微信JSSDK-下载到自己的服务器-ffmpeg转码h5播放)

前段时间公司要求做一个录音功能。没有头绪,到网上查了下,发现微信的api提供的jssdk能满足我的需求,就决定用这个开发了。先后查了不少资料加上自己琢磨和问前辈的经验,花了大概一个多星期总算完成了,这边记录一下过程。


首先贴一篇教程出来,这篇教程前期省了我不少时间。

微信JSSDK中Config配置:http://blog.csdn.net/zhaojiacan/article/details/51025097 这篇讲的很详细,跟着配置是没问题的,

主要注意的有两点,其一是保证你的项目运行环境是你配置的js安全接口域名之下。其二是链接里面出现的util工具类他没有上传,里面

封装的都是些普通的写入写出方法,自己写都可以这不是重点。

jssdk配置成功后,就可以直接引入微信接口了。


wx.ready(function(){

	    /** 
	    config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,
	       所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,
	       则可以直接调用,不需要放在ready函数中。
	    */
	    
	    var localIds
	    var lysj //录音时间
	    var lyCss //样式
	    var lyName //图片名字
	    var localIdArr //本地ID集合字符串
		var serverIdArr //服务端ID集合字符串

		
		//注册微信播放录音结束事件【一定要放在wx.ready函数内】
		/* wx.onVoicePlayEnd({
		    success: function (res) {
		    }
		}); */
	    
		//假设全局变量已经在外部定义
		//按下开始录音
		$('#talk_btn').on('touchstart', function(event){
		    event.preventDefault();
		    START = new Date().getTime();
		    if(localIds != null){
		    	if(localIdArr == null){
		    		localIdArr = localIds
		    	}else{
		    		localIdArr += "," + localIds;
		    	}
		    }
		    localIds = null;
		    recordTimer = setTimeout(function(){
		        wx.startRecord({
		            success: function(){
		                localStorage.rainAllowRecord = 'true';
		                wx.onVoiceRecordEnd({
		                    // 录音时间超过一分钟没有停止的时候会执行 complete 回调
		                    complete: function (res) {
		                    	END = new Date().getTime();
		                        localIds = res.localId;
		                        alert('最多只能录制一分钟');
		                    }
		                }) 
		            },
		            cancel: function () {
		                alert('用户拒绝授权录音');
		            }
		        });
		    },300);
		});
		
		var yyTotal = 1; //语音数量
		//松手结束录音
		$('#talk_btn').on('touchend', function(event){
		    event.preventDefault();
		    if(localIds != null){
		    	
		    }else{
		    	END = new Date().getTime();	
		    }
		    
		    
		    if((END - START) < 300){
		        END = 0;
		        START = 0;
		        //小于300ms,不录音
		        clearTimeout(recordTimer);
		        alert("录音时间太短,录音不做保存!")
		    }else{
		        $("#voiceButton").show();
		        lysj = (END - START)/1000;
		        if(lysj <= 10){
		        	lyName = 10;
		        	lyCss = "height: 42px;width: 79px;";
		        }else if(10 < lysj && lysj <= 20){
		        	lyName = 20;
		        	lyCss = "height: 42px;width: 129px;";
		        }else if(20 < lysj && lysj <= 30){
		        	lyName = 30;
		        	lyCss = "height: 42px;width: 177px;";
		        }else if(30 < lysj && lysj <= 40){
		        	lyName = 40;
		        	lyCss = "height: 42px;width: 227px;";
		        }else if(40 < lysj && lysj <= 50){
		        	lyName = 50;
		        	lyCss = "height: 42px;width: 277px;";
		        }else if(50 < lysj && lysj <= 60){
		        	lyName = 60;
		        	lyCss = "height: 42px;width: 327px;";
		        }else{
		        	lyName = 60;
		        	lyCss = "height: 42px;width: 327px;";
		        	lysj = "60.00";
		        }
		        lysj = ""+ lysj +"";
		        lysj = lysj.substr(0,lysj.lastIndexOf("."));
		        
		        wx.stopRecord({
		          success: function (res) {
		        	if(localIds != null){
		        		
		  		    }else{
		        		localIds = res.localId;
		  		    }
		            if(yyTotal == 1){
			        	$("#rwnrVoice").append("
  • "+ lysj +"s

    " + "" + "
  • "); yyTotal = 2; }else{ $("#rwnrVoice").append("
  • "+ lysj +"s

    " + "" + "
  • "); yyTotal++; } playVoice(localIds); }, fail: function (res) { alert(JSON.stringify(res)); }, }); } }); //播放语音接口 function playVoice(obj){ wx.playVoice({ localId: obj // 需要播放的音频的本地ID,由stopRecord接口获得 }); } //播放语音接口 $('#_playVoice').on('click',function(){ wx.playVoice({ localId: localIds // 需要播放的音频的本地ID,由stopRecord接口获得 }); }) //暂停播放接口 $('#_pauseVoice').on('click',function(){ wx.pauseVoice({ localId: localIds // 需要播放的音频的本地ID,由stopRecord接口获得 }); }) //停止播放接口 $('#_stopVoice').on('click',function(){ wx.stopVoice({ localId: localIds // 需要播放的音频的本地ID,由stopRecord接口获得 }); }) //上传录音接口 $('#_uploadVoice').on('click',function(){ var result = confirm("确定上传所有录音吗?"); if(result == true){ //只有一段录音直接上传 if((yyTotal - 1) == 1){ uploadVoice(localIds); }else{//多段录音,遍历数组上传 //删除最后一段录音 if(localIds == null){ }else{ localIdArr += "," + localIds; } var strs= new Array(); strs = localIdArr.split(","); for (i=0;i

    这边是我调用到的接口,前端辞职了,我也就知道把样式写上来了,将就看。因为这边考虑到用户会多条语音上传,且不用用户录一条点一次上传这么麻烦,这边就创建几个全局变量,录好的本地id在存在变量里。用户点上传的时候直接全部上传。同时把返回的serverId也是放到一个集合里在表单提交到后台下载到自己服务器上。


    接着是后台下载微信临时资源到本地服务器比且转码(我们上传的录音),因为下载的资源是amr格式的,h5不支持播放,我们得转码,为了不浪费服务器的内存,转码后顺便把amr格式的录音删了。

     /** 
         *  
         * 根据文件id下载文件 
         *  
         *  
         *  
         * @param mediaId 
         *  
         *            媒体id 
         *  
         * @throws Exception 
         */  
      
        public static String getInputStream(String mediaId) {  
        	InputStream is = null;
        	File amrPath = null;
        	File mp3Path = null;
        	String access_token = PropertiesLoader.readProperties("access_token", "wxHelp.properties");
            String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token="  
                    + access_token + "&media_id=" + mediaId;
            try {
                URL urlGet = new URL(url);  
                HttpURLConnection http = (HttpURLConnection) urlGet  
                        .openConnection();  
                http.setRequestMethod("GET"); // 必须是get方式请求  
                http.setRequestProperty("Content-Type","audio/mp3");  
                http.setDoOutput(true);  
                http.setDoInput(true);  
                System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒  
                System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒  
                http.connect();  
                // 获取文件转化为byte流  
                is = http.getInputStream();
                
                //获取项目路径
                String path = Thread.currentThread().getContextClassLoader().getResource("").toString(); 
            	path = path.replace('/', '\\'); // 将/换成\  
            	path = path.replace("file:", ""); //去掉file:  
            	path = path.replace("classes\\", ""); //去掉classes\  
            	path = path.replace("target\\", ""); //去掉target\  
            	path = path.replace("WEB-INF\\", "");//去掉web-inf\
            	path = path.substring(1); //去掉第一个\,如 \D:\JavaWeb... 
            	//文件添加下级目录地址
            	path += "static"+File.separator +"common" + File.separator +"voice";
                UUID uuid = UUID.randomUUID();
                String fileName = uuid.toString().replace("-", "");
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                File file = new File(path);
                File todayFile = new File(path + "\\" + sdf.format(new Date()));
                amrPath = new File(todayFile + "\\" + fileName + ".amr");
                mp3Path = new File(amrPath.toString().replace(".amr", ".mp3"));
            	//如果文件夹不存在则创建    
            	if  (!file.exists()  && !file.isDirectory()){
            	    file.mkdir();
            	}
            	if  (!todayFile.exists()  && !todayFile.isDirectory()){
            		todayFile.mkdir();
            	}
            	
                BufferedInputStream in = new BufferedInputStream(is);
                BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(amrPath));
                byte[] by = new byte[1024];
                int lend = 0;
                while((lend = in.read(by)) != -1){
                    out.write(by,0,lend);
                }
                in.close();
                out.close();
                //转码
                changeToMp3(amrPath.toString(),mp3Path.toString());
                //删除amr文件
                if(amrPath.isFile() && amrPath.exists()){
                	amrPath.delete();
                }
            } catch (Exception e) {
                e.printStackTrace();  
            }
            
            //返回一个数据库存储的格式 :wgBank\static\common\voice\2017-10-23\3d1043b7dafe44a78cf4f2e45047d999.mp3
            String newPath = mp3Path.toString().replace("\\", "/");
            newPath = newPath.substring(newPath.toString().lastIndexOf("/wgBank"));
            return newPath;
      
        }
        
        
        public static void changeToMp3(String sourcePath, String targetPath) {    
            File source = new File(sourcePath);    
            File target = new File(targetPath);    
            AudioAttributes audio = new AudioAttributes();    
            Encoder encoder = new Encoder();    
        
            audio.setCodec("libmp3lame");   
            EncodingAttributes attrs = new EncodingAttributes();    
            attrs.setFormat("mp3");    
            attrs.setAudioAttributes(audio);    
        
            try {    
                encoder.encode(source, target, attrs);
            } catch (IllegalArgumentException e) {    
                e.printStackTrace();    
            } catch (InputFormatException e) {    
                e.printStackTrace();    
            } catch (EncoderException e) {    
                e.printStackTrace();    
            }    
        }  
        

    至此,返回的路径就可以存在数据库了,展示的时候直接数据库获取路径,然后h5播放就可以了。

    								
    									
    							      	
  • 语音详情:

  • =========================================开发过程中遇到的一些问题和解决方法=============================================================

    1:防止长按button时,自动触发系统事件(复制,拷贝,剪切啥的)。给button加个样式:ontouchstart="return false;"

    2:连续多次点击播放的时候,会导致微信闪退卡死。所以播放之前最好先初始化一下录音

    	//播放语音接口 播放的时候先初始化
    	function playVoice2(obj){
            var ua = navigator.userAgent.toLowerCase();	
        	if (/iphone|ipad|ipod/.test(ua)) {
        		wx.stopVoice({
    				localId: obj // 需要播放的音频的本地ID,由stopRecord接口获得
    			});
        	} else if (/android/.test(ua)) {
        		wx.stopVoice({
    				localId: obj // 需要播放的音频的本地ID,由stopRecord接口获得
    			});
        	}
    		wx.playVoice({
    			localId: obj // 需要播放的音频的本地ID,由stopRecord接口获得
    		});
    	}
    3:时间过短不录音和超过一分钟停止录音,上面代码已经有解决方法。

    4:下载下来的时候,电脑也播放不了。这个问题卡了挺久,后来给前辈看了下我的代码才发现问题。

    一开始写的是"application/x-www-form-urlencoded"的json文本形式,但是我下的是音频,所以一直都是文件损坏,播放不了。

    视频:video/mpeg4

    音频:audio/mp3

    5:最最最乌龙 卡我最最最长时间 也是解决方法中最最最简单的,~~用错api了,我开发的是企业号,但是我用的是服务号的api,之前一直不知道企业号和服务号的api是分开的


    服务号api:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432

    企业号api:http://qydev.weixin.qq.com/wiki/index.php?title=%E9%A6%96%E9%A1%B5


    (微信接口开发,测试肯定是要在外网上才能测试,如何本地测试接口呢,就得把本机的ip封装成正规域名,然后修改微信后排的js安全接口。这样就可以测试拉。)

    推荐大家用这款工具:natapp,官网有教程~成功的界面大致是这样的,这样可以直接访问域名:h4g4rc.natappfree.cc来访问到本机了。







    你可能感兴趣的:(微信录音,下载,转码,jssdk)