关于wesocket大文件通讯的切片实现方法

关于websocket的实现网上很多资料这里就不详说,这里大概讲我在websocket传输大文件的时的方法,websocket传输单个文件最大不能超过7kg,否则前段自动断掉,当我们用来语音通讯时,通常语音文件都比较大,传输单个语音文件显然是不现实的,网上查了关于微信的语音实现,当然具体的源码是看不到的,不过有人亲测过微信语音大概的实现过程。

微信实现语音的过程是边录音边传输,把一段语音切割成很多个小片段的语音传输到后台,后台在进行合并处理,后台向前段传输语音时同理,我的项目中大概实现如下:

前段用recorder.js实现浏览器录音,录音完成后得到语音文件为blob,blob是js里的一个大文件对象,是原始二进制数据,实现为

var blob = new Blob(chuanks[],{type:"audio/wav"});

 

其中chuanks[]可以示多种数据类型,arraybuffer,blob等,我的实现方法是把blob大文件通过bolb.slice()切割成多个小blob文件,然后用

var reader = new FileReader();把文件转成base64传输到后台合并处理,blob文件只能通过FileReader对象来读取文件内容,下面直接上代码

   //前台
recorder && recorder.exportWAV(function(blob) { //将文件转为base64 console.log(blob); var type = "audio/wav"; var chunk = 5 * 1024; var messageid = new Date().toISOString() + RndNum(5); var chunks = []; var start = 0; var islast = false; //文件切割 for (var i = 0; i < Math.ceil(blob.size / chunk); i++) { var end = start + chunk; chunks[i] = blob.slice(start , end, type); start = end; if(blob.size<end){islast=true;} send(chunks[i], type, messageid, i, islast); } //发送文件 function send(val,type, messageid, i, islast){ var reader = null; var postValue={} reader = new FileReader() reader.readAsDataURL(val,"UTF-8");//转成base64 reader.onload = function () { str = reader.result.split(",")[1]; postValue.voicetype=type; postValue.content=str; postValue.messageid=messageid; postValue.sequence=i; postValue.islast=islast; socket.send(JSON.stringify(postValue));//websocket发送文件 }; }

//后台接收
String messageId = req.getMessageid();
boolean isLast = req.isIslast();
int sequence = req.getSequence();
String content = req.getContent();
org.json.JSONObject result = null;
try {
SplitMessage saveMessage = spiltmessages.get(messageId);
if(isLast==true && saveMessage==null ) {
result = BaiduIntelligeVoiceUtil.asr(content,req.getVoicetype());
}else{
if((saveMessage==null ||saveMessage.getContent()==null) && isLast==false && sequence==0){
byte[] splitBytes = Base64Utils.decodeFromString(content);
SplitMessage splitMessageNew = new SplitMessage(sequence,splitBytes);
spiltmessages.put(messageId,splitMessageNew);
return;
}else if(saveMessage !=null && saveMessage.getContent()!=null && isLast==false){
byte[] saveContent = saveMessage.getContent();
if((sequence)!=saveMessage.getSequence()+1){
SimpleResponse simpleResponse = SimpleResponse.failureResp("接收语音文件格式转化时发生错误","text");
return;
}
byte[] splitBytes = Base64Utils.decodeFromString(content);
//新保存的字节数组
byte[] saveBytesNew = new byte[splitBytes.length+saveContent.length];
System.arraycopy(saveContent,0,saveBytesNew,0,saveContent.length);
System.arraycopy(splitBytes, 0, saveBytesNew, saveContent.length, splitBytes.length);
saveMessage.setContent(saveBytesNew);
saveMessage.setSequence(sequence);
return;
}else if(saveMessage !=null && saveMessage.getContent()!=null && isLast==true){
if((sequence-1)!=saveMessage.getSequence()){
SimpleResponse simpleResponse = SimpleResponse.failureResp("接收语音文件格式转化时发生错误","text");
sendMessageToUser(clientId, new TextMessage(JSONObject.toJSONString(simpleResponse)));
return;
}
byte[] saveContent = saveMessage.getContent();
byte[] splitBytes = Base64Utils.decodeFromString(content);
//新保存的字节数组
byte[] saveBytesNew = new byte[splitBytes.length+saveContent.length];
System.arraycopy(saveContent,0,saveBytesNew,0,saveContent.length);
System.arraycopy(splitBytes, 0, saveBytesNew, saveContent.length, splitBytes.length);
spiltmessages.remove(messageId);

//TODO 解析语音成文字
result = BaiduIntelligeVoiceUtil.asrForSplit(saveBytesNew,"pcm",16000,null);
}else {
SimpleResponse simpleResponse = SimpleResponse.failureResp("对不起,不能解析您的语音","text");
sendMessageToUser(clientId, new TextMessage(JSONObject.toJSONString(simpleResponse)));
return;
}
}

}catch (IOException e){
logger.error("clientId为 :"+clientId + " 的用户语音文件格式转化时发生错误 : " + e.toString());
SimpleResponse simpleResponse = SimpleResponse.failureResp("语音文件格式转化时发生错误","text");
sendMessageToUser(clientId, new TextMessage(JSONObject.toJSONString(simpleResponse)));
return;
} catch (InterruptedException e) {
logger.error("clientId为 :"+clientId + " 的用户语音文件格式转化时发生错误 : " + e.toString());
SimpleResponse simpleResponse = SimpleResponse.failureResp("语音文件格式转化时发生错误","text");
sendMessageToUser(clientId, new TextMessage(JSONObject.toJSONString(simpleResponse)));
return;
}




后台发送
//
TODO 合成回答语音 TtsResponse syncResp = BaiduIntelligeVoiceUtil.synthesis(respmessage); byte[] bytes = syncResp.getData(); if(bytes!=null){ List<byte[]> byteList = ByteMergeAndSplitUtil.splitBytesBySize(bytes,5120); for(int i=0;i<byteList.size();i++){ String spilitMsg = Base64Utils.encodeToString(byteList.get(i)); SimpleResponse simpleResponse = SimpleResponse.successResp(spilitMsg,"mp3"); simpleResponse.setMessageid(messageId); simpleResponse.setSequence(i); if(i<byteList.size()-1) { sendMessageToUser(clientId, new TextMessage(JSONObject.toJSONString(simpleResponse))); }else { simpleResponse.setIslast(true); simpleResponse.setVoicetotext(question); sendMessageToUser(clientId, new TextMessage(JSONObject.toJSONString(simpleResponse))); } } return; }else { SimpleResponse simpleResponse = SimpleResponse.failureResp("语音合成失败", "text"); sendMessageToUser(clientId, new TextMessage(JSONObject.toJSONString(simpleResponse))); return; }
//前台接收信息
    var chunks = [];
    socket.onmessage = function (msg) {
        console.log(msg.data);
        var str = {};
        var reVal = JSON.parse(msg.data);
            str.content = reVal.content;
            str.errno = reVal.errno;
            str.islast = reVal.islast;
            str.sequence = reVal.sequence;
            str.type = reVal.type;
            chunks[reVal.sequence] = base64ToBlob(str.content);
            if(str.islast){
                var blob = new Blob(chunks,{type : reVal.type});
                var url = URL.createObjectURL(blob);
                var voi = document.getElementById('voi');
                var au = document.createElement('audio');
                var div = document.createElement('div');
                  
                au.controls = true;
                au.src = url;
                voi.appendChild(div);
                div.appendChild(au);
                div.style = "float:left;clear:both;";
                voi.scrollTop = voi.scrollHeight;//使滚动条一直在底部
                openPage(reVal.voicetotext);
            }
        
        
    }

 借鉴https://segmentfault.com/a/1190000011563430

posted @ 2018-09-19 10:45  MJBrian  阅读(1250)  评论(0编辑  收藏  举报