SQLServer-实现简单的省市区联动

47次阅读

共计 12143 个字符,预计需要花费 31 分钟才能阅读完成。

在分享之前先贴上借鉴的大佬们的博客,感谢这些巨人,关于录音: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; // 实现 WebSocket

var 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…

正文完
 0