在分享之前先贴上借鉴的大佬们的博客,感谢这些巨人,关于录音:https://blog.csdn.net/sweetsuzyhyf/article/details/50469881

今天研究了一下SQL Server实现省市区联动的方法,记录一下。

一、先创建三个表,Dic_Area(区)、Dic_City(市)和Dic_Province(省),三个表建表语句如下:

Dic_Province

Dic_City

Dic_Area

插入数据,(数据来源https://www.cnblogs.com/zhu520/p/8244578.html),因为实在是有点懒,只能网上借用一下这位兄台的数据了。插入数据就不说了,复制或者下载过来改一下表名执行就ok。

二、新建一个Windows窗体应用,插入3个ComboBox,命名分别为:cbo_Province、cbo_City、cbo_District。

先声明一个连接字符串:

1 private string connStr = @"Data Source=SD-20191219LHFX\SQLEXPRESS;Initial Catalog=AreaSelection;User ID=sa;Password=***********"; //连接字符串

接下来在Form1_Load中添加预加载代码,程序运行的时候ComboBox中就要加载显示地区信息。

1 private void Form1_Load(object sender, EventArgs e) 2 { 3 LoadingProvice(); 4 }

为了代码清晰,我把省市区分别封装成三个方法:LoadingProvice()、LoadingCity()、LoadingDistrict()。

但是在这之前,我们要先构造一个集合,因为cbo_Province可以调用DataSource这个方法。三个表中,每个表都有ID和地区名字,所以新建一个类AreaInfo.cs就可以了:

在AreaInfo中添加下面的代码:

1 public partial class AreaInfo 2 { 3 public int Id { get; set; } //ID
4 public string Title { get; set; } //地区名字
5 }

接下来就可以写方法了:(以下分别是省、市、区)

; "复制代码")

1 private void LoadingProvice() 2 {
3 List<AreaInfo> list = new List<AreaInfo>();
4 using(SqlConnection conn = new SqlConnection(connStr)) 5 {
6 string sql = "select * from Dic_Province";
7 SqlCommand cmd = new SqlCommand(sql, conn); 8 conn.Open();
9 SqlDataReader reader = cmd.ExecuteReader(); 10
11 while (reader.Read()) 12 { 13 //添加元素
14 list.Add(new AreaInfo() 15 { 16 //初始化器
17 Id = Convert.ToInt32(reader["ProvinceID"]), 18 Title = reader["Province"].ToString() 19 }); 20 } 21 } 22 cbo_Province.DisplayMember = "Title"; //显示属性
23 cbo_Province.ValueMember = "Id"; //值属性
24 cbo_Province.DataSource = list; 25 }

; "复制代码")

; "复制代码")

1 private void LoadingCity() 2 {
3 int pid = Convert.ToInt32(cbo_Province.SelectedValue); 4 List<AreaInfo> list = new List<AreaInfo>();
5 string sql = "select * from Dic_City where provinceid = @pid";
6 using(SqlConnection conn = new SqlConnection(connStr)) 7 {
8 SqlCommand cmd = new SqlCommand(sql, conn); 9 cmd.Parameters.Add(new SqlParameter("@pid", pid)); 10 conn.Open(); 11 SqlDataReader reader = cmd.ExecuteReader(); 12 while(reader.Read()) 13 { 14 list.Add(new AreaInfo() 15 { 16 Id = Convert.ToInt32(reader["cityID"]), 17 Title = reader["city"].ToString() 18 }); 19 } 20 } 21 cbo_City.DisplayMember = "Title"; 22 cbo_City.ValueMember = "Id"; 23 cbo_City.DataSource = list; 24 }

; "复制代码")

; "复制代码")

1 private void LoadingDistrict() 2 {
3 List<AreaInfo> list = new List<AreaInfo>();
4 int cid = Convert.ToInt32(cbo_City.SelectedValue); 5 string sql = "select * from Dic_Area where cityID = @cid";
6 using(SqlConnection conn = new SqlConnection(connStr)) 7 {
8 SqlCommand cmd = new SqlCommand(sql, conn); 9 cmd.Parameters.Add(new SqlParameter("@cid", cid)); 10 conn.Open(); 11 SqlDataReader reader = cmd.ExecuteReader(); 12 while(reader.Read()) 13 { 14 list.Add(new AreaInfo() 15 { 16 Id = Convert.ToInt32(reader["areaID"]), 17 Title = reader["area"].ToString() 18 }); 19 } 20 } 21 cbo_District.DisplayMember = "Title"; 22 cbo_District.ValueMember = "Id"; 23 cbo_District.DataSource = list; 24 }

; "复制代码")

每个方法大同小异,都是对数据库的基本操作,就没进行过多的注释。

最后在comboBox的SelectedIndexChanged方法中添加方法名,如下:

1 private void cbo_provice_SelectedIndexChanged(object sender, EventArgs e) 2 { 3 LoadingCity(); 4 LoadingDistrict(); 5 }

1 private void cbo_City_SelectedIndexChanged(object sender, EventArgs e) 2 { 3 LoadingDistrict(); 4 }

cbo_District控件下面就不需要添加方法了,因为区下面就没有分类了。

完成界面如下:


关于波形图:https://blog.csdn.net/weixin_44204580/article/details/90112055

 https://blog.csdn.net/qq_32447301/article/details/84948717?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

废话:想不到我的第一篇博客是关于前端,作为一名后端的小菜,前端方面肯定还有很多不足之处,如果文章有任何问题欢迎指正。感谢大家。好了!废话不多说下面讲一下需求。

需求:公司要求实现web端的录音并通过websocket实时上传至java后台,而且能通过vlc实时播放,简单一点讲就是我用网页在那一边讲话,一个大喇叭就能实时把我的话播出去,这样是不是通俗易懂呀,而且呢公司要求用mp3格式。当然啦!为了知道自己在讲话需要一个波形图,这里主要实现前半部分功能,后半部分臣妾也做不到呀!后半部分的vlc播放呢如果大家想知道,可以留言,届时可以给大家指条明路

前端实现:

引入:<script type="text/javascript" src="/js/recorder/recordmp3.js"></script>

这个跟大佬的js有点不一样,我在里面加了一点东西,而且在这个js里面引入了两个另外的js,lame.min.js和worker-realtime.js,这俩在大佬的代码里有

页面:

; "复制代码")

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"\>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>

<meta http-equiv\="Content-Type" content\="text/html; charset=gb2312"/><title\>测试</title\>

</head>
<body>
<button id="intercomBegin">开始对讲</button>
<button id="intercomEnd">关闭对讲</button>
<canvas id="casvased" ></canvas>
</body>
<script type="text/javascript" src="/js/jquery-3.3.1.js"></script>
<script type="text/javascript" src="/js/recorder/recordmp3.js"></script>
<script type="text/javascript">

var begin \= document.getElementById('intercomBegin'); var end \= document.getElementById('intercomEnd'); var canvas \= document.getElementById("casvased"); var canvasCtx \= canvas.getContext("2d"); var ws \= null; //实现WebSocketvar recorder; /\* \* WebSocket \*/function useWebSocket() {    ws \= new WebSocket("ws://127.0.0.1:8089/send/voice");    ws.binaryType \= 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据

ws.onopen = function () {

console.log('握手成功'); if (ws.readyState \== 1) { //ws进入连接状态,则每隔500毫秒发送一包数据

recorder.start();

        }    };    ws.onmessage \= function (msg) {        console.info(msg)    }    ws.onerror \= function (err) {        console.info(err)    }} /\* \* 开始对讲 \*/ begin.onclick \= function () {    recorder \= new MP3Recorder({        debug: true,        funOk: function () {            console.log('点击录制,开始录音! ');        },        funCancel: function (msg) {            console.log(msg);            recorder \= null;        }    });} /\* \* 关闭对讲 \*/ end.onclick \= function () { if (ws) {        ws.close();        recorder.stop();        console.log('关闭对讲以及WebSocket');    }} var sendData \= function() { //对以获取的数据进行处理(分包)    var reader \= new FileReader();    reader.onload \= e \=> { var outbuffer \= e.target.result; var arr \= new Int8Array(outbuffer); if (arr.length \> 0) { var tmparr \= new Int8Array(1024); var j \= 0; for (var i \= 0; i < arr.byteLength; i++) {                tmparr\[j++\] \= arr\[i\]; if (((i + 1) % 1024) \== 0) {                    ws.send(tmparr); if (arr.byteLength \- i \- 1 \>= 1024) {                        tmparr \= new Int8Array(1024);                    } else {                        tmparr \= new Int8Array(arr.byteLength \- i \- 1);                    }                    j \= 0;                } if ((i + 1 \== arr.byteLength) && ((i + 1) % 1024) != 0) {                    ws.send(tmparr);                }            }        }    };    recorder.getMp3Blob(function (blob) {        reader.readAsArrayBuffer(blob);//这里拿到mp3格式的音频流写入到reader中     })

}; </script> </html>

; "复制代码")

recordmp3.js

; "复制代码")

(function (exports) { var MP3Recorder = function (config) { var recorder = this;

    config \= config || {};    config.sampleRate \= config.sampleRate || 44100;    config.bitRate \= config.bitRate || 128;    navigator.getUserMedia \= navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; if (navigator.getUserMedia) {        navigator.getUserMedia({                audio: true }, function (stream) { var context = new AudioContext(),                    microphone \= context.createMediaStreamSource(stream),                    processor \= context.createScriptProcessor(16384, 1, 1),//bufferSize大小,输入channel数,输出channel数

mp3ReceiveSuccess, currentErrorCallback; var height = 100; var width = 400;

                const analyser \= context.createAnalyser()                analyser.fftSize \= 1024                //连接到音频源

microphone.connect(analyser);

                analyser.connect(context.destination);                const bufferLength \= analyser.frequencyBinCount // 返回的是 analyser的fftsize的一半                const dataArray = new Uint8Array(bufferLength); function draw() {                    canvasCtx.clearRect(0, 0, width, height); //清除画布                    analyser.getByteFrequencyData(dataArray); // 将当前频率数据复制到传入其中的Uint8Array                    const requestAnimFrame = window.requestAnimationFrame(draw) || window.webkitRequestAnimationFrame(draw);                    canvasCtx.fillStyle \= '#000130';                    canvasCtx.fillRect(0, 0, width, height);                    let barWidth \= (width / bufferLength) \* 2;                    let barHeight;                    let x \= 0;                    let c \= 2                    for (let i = 0; i < bufferLength; i++) {                        barHeight \= c+(dataArray\[i\]/400)\*height;                        canvasCtx.fillStyle = 'rgb(0, 255, 30)';                        canvasCtx.fillRect(x, height / 2 - barHeight / 2, barWidth, barHeight);                        x += barWidth + 1;                    }                }                draw();                useWebSocket();                config.sampleRate \= context.sampleRate;                processor.onaudioprocess \= function (event) { //边录音边转换                    var array = event.inputBuffer.getChannelData(0);                    realTimeWorker.postMessage({cmd: 'encode', buf: array});                    sendData();                }; var realTimeWorker = new Worker('/js/recorder/worker-realtime.js');                realTimeWorker.onmessage \= function (e) { switch (e.data.cmd) { case 'init':                            log('初始化成功'); if (config.funOk) {                                config.funOk();                            } break; case 'end':                            log('MP3大小:', e.data.buf.length); if (mp3ReceiveSuccess) {                                mp3ReceiveSuccess(new Blob(e.data.buf, {type: 'audio/mp3'}));                            } break; case 'error':                            log('错误信息:' + e.data.error); if (currentErrorCallback) {                                currentErrorCallback(e.data.error);                            } break; default:                            log('未知信息:', e.data);                    }                };                recorder.getMp3Blob \= function (onSuccess, onError) {                    currentErrorCallback \= onError;                    mp3ReceiveSuccess \= onSuccess;                    realTimeWorker.postMessage({cmd: 'finish'});                };                recorder.start \= function () { if (processor && microphone) {                        microphone.connect(processor);                        processor.connect(context.destination);                        log('开始录音');                    }                }                recorder.stop \= function () { if (processor && microphone) {                        microphone.disconnect();                        processor.disconnect();                        log('录音结束');                    }                }                realTimeWorker.postMessage({                    cmd: 'init',                    config: {                        sampleRate: config.sampleRate,                        bitRate: config.bitRate                    }                });            }, function (error) { var msg; switch (error.code || error.name) { case 'PERMISSION\_DENIED': case 'PermissionDeniedError':                        msg \= '用户拒绝访问麦客风'; break; case 'NOT\_SUPPORTED\_ERROR': case 'NotSupportedError':                        msg \= '浏览器不支持麦客风'; break; case 'MANDATORY\_UNSATISFIED\_ERROR': case 'MandatoryUnsatisfiedError':                        msg \= '找不到麦客风设备'; break; default:                        msg \= '无法打开麦克风,异常信息:' + (error.code || error.name); break;                } if (config.funCancel) {                    config.funCancel(msg);                }            });    } else { if (config.funCancel) {            config.funCancel('当前浏览器不支持录音功能');        }    } function log(str) { if (config.debug) {            console.log(str);        }    }}exports.MP3Recorder \= MP3Recorder;

})(window);

; "复制代码")

后端websocket:

这里实现的是保存为mp3文件

; "复制代码")

package com.jetosend.common.socket; import com.jetosend.common.utils.Utils; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.*; import java.nio.ByteBuffer; import java.util.Hashtable; import java.util.Map;

@ServerEndpoint("/send/{key}")
@Component public class ServerSocket { private static final Map<String, Session> connections = new Hashtable<>();

ByteArrayOutputStream byteArrayOutputStream \= new ByteArrayOutputStream(); /\*\*\* \* @Description:打开连接 \* @Param: \[id, 保存对方平台的资源编码 \* session\] \* @Return: void \* @Author: Liting \* @Date: 2019-10-10 09:22 \*/ @OnOpen public void onOpen(@PathParam("key") String id, Session session) {    System.out.println(id \+ "连上了");    connections.put(id, session);} /\*\* \* 接收消息 \*/ @OnMessage public void onMessage(@PathParam("key") String id, InputStream inputStream) {    System.out.println("来自" + id); try { int rc = 0; byte\[\] buff = new byte\[100\]; while ((rc = inputStream.read(buff, 0, 100)) > 0) {            byteArrayOutputStream.write(buff, 0, rc);        }    } catch (Exception e) {        e.printStackTrace();    }} /\*\* \* 异常处理 \* \* @param throwable \*/ @OnError public void onError(Throwable throwable) {    throwable.printStackTrace(); //TODO 日志打印异常

} /** * 关闭连接 */ @OnClose public void onClose(@PathParam("key") String id) {

    System.out.println(id \+ "断开");    BufferedOutputStream bos \= null;    FileOutputStream fos \= null;    File file \= null; try {        file \= new File("D:\\\\testtest.mp3"); //输出流        fos = new FileOutputStream(file); //缓冲流        bos = new BufferedOutputStream(fos); //将字节数组写出

bos.write(byteArrayOutputStream.toByteArray());

    } catch (Exception e) {        e.printStackTrace();    } finally { if (bos != null) { try {                bos.close();            } catch (IOException e) {                e.printStackTrace();            }        } if (fos != null) { try {                fos.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    connections.remove(id);}

; "复制代码")

实现效果:

阅读 31发布于 6月12日

操作

举报

收藏

分享

本作品系 原创 , 采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议


纪莫
  • 1

关注作者

0 条评论

得票时间

提交评论

推荐阅读

[

Web 实时推送技术的总结

随着 Web 的发展,用户对于 Web 的实时推送要求也越来越高 ,比如,工业运行监控、Web 在线通讯、即时报价系统、在线游戏等,都需要将后台发生的变化主动地、实时地传送到浏览器端,而不需要用户手动地刷新页面。...

浪里行舟 阅读 6.3k 105 赞 10 评论

](https://segmentfault.com/a/11...[

WebSocket 与 Polling , Long-Polling , Streaming 的比较!

Web Sockets定义了一种在通过一个单一的 socket 在网络上进行全双工通讯的通道。它不仅仅是传统的 HTTP 通讯的一个增量的提高,尤其对于实时、事件驱动的应用来说是一个飞跃。

前端小智 阅读 3.8k 56 赞

](https://segmentfault.com/a/11...[

JavaScript是如何工作: 深入探索 websocket 和HTTP/2与SSE +如何选择正确的路径!

文章底部分享给大家一套 react + socket 实战教程 这是专门探索 JavaScript 及其所构建的组件的系列文章的第5篇。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 如果你错过了前面的章节,可以...

前端小智 阅读 4.8k 45 赞 2 评论

](https://segmentfault.com/a/11...[

WebSocket实战:在 Node 和 React 之间进行实时通信

Web 为了支持客户端和服务器之间的全双工(或双向)通信已经走过了很长的路。这是 WebSocket 协议的主要目的:通过单个 TCP 套接字连接在客户端和服务器之间提供持久的实时通信。

疯狂的技术宅 阅读 3.7k 28 赞

](https://segmentfault.com/a/11...[

WebSocket 原理

以前的网站为了实现推送功能,使用的方法都是轮询。所谓的轮询就是在特定的时间间隔(例如1秒),由浏览器向服务器发出一个 Http request,然后服务器返回最新的数据给客户端浏览器,从而给出一种服务端实时推送...

chengjianhua 阅读 2.7k 13 赞

](https://segmentfault.com/a/11...[

WebSocket+MSE——HTML5 直播技术解析

常见的可用于 HTML5 的直播技术有 HLS、WebSocket 与 WebRTC。今天我向大家介绍WebSocket 与 MSE 相关的技术要点,并在最后通过一个实例来展示具体用法。

云叔_又拍云 阅读 4.7k 8 赞 1 评论

](https://segmentfault.com/a/11...[

Web Socket & Socket.io

我们可以非常轻松的捕获浏览器上发生的事件(比如用户点击了盒子),这个事件可以轻松产生与服务器的数据交互(比如Ajax)。但是,反过来却是不可能的:服务器端发生了一个事件,服务器无法将这个事件的信息实时...

alogy 阅读 3k 8 赞

](https://segmentfault.com/a/11...[

Spring4.0 构建websocket

简单轮询 这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁

](https://segmentfault.com/a/11...