来访人员登记系统(十)网页端使用websocket向C#服务端传送图片和文字

前置模块

来访人员登记系统(二)网页客户端的实现和websocket通信

本文主要在上文的基础上增加了网页端的图片提交,后台的图片转码处理和发送,服务端的图片复原和保存等功能。

实现思路是使用html的input标签的file类型接收上传的图片,在后台将图片用base64码的形式转成string类型,像发送字符串一样发到服务端,服务端处理好base64码的前缀内容信息,并解码复原图片保存到服务器上。


HTML

HTML在前置模块的基础上增加了input标签,并修改了CSS样式使之布局更加合理。

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta charset="utf-8" />
    <title>XXXX股份有限公司主机房人员出入申请单</title>
    <link rel="stylesheet" href="style.css" />
    <script type="text/javascript" src="script.js"></script>
</head>

<body>
    <br />
    <h1>XXXX股份有限公司</h1>
    <h1>主机房人员出入申请单</h1>

    <div>
        <form id="sendForm">
            <table>
                <tr>
                    <td><input id="staff" class="text" type="text" placeholder="弘业对接人员" required /></td>
                </tr>
                <tr>
                    <td><input id="name" class="text" type="text" placeholder="外单位人员" required /></td>
                </tr>
                <tr>
                    <td><input id="company" class="text" type="text" placeholder="单位名称" required /></td>
                </tr>
                <tr>
                    <td><input id="idcard" class="text" type="text" placeholder="身份证号" required pattern="/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/" /></td>
                </tr>
                <tr>
                    <td class="imgtext"><span>身份证国徽面照片:</span></td>
                </tr>
                <tr>
                    <td><img class="preview" id="preview1"/></td>
                </tr>
                <tr>
                    <td><input type="file" accept="image/*" name="img1file" id="img1file" required /></td>
                </tr>
                <tr>
                    <td class="imgtext"><span>身份证人像面照片:</span></td>
                </tr>
                <tr>
                    <td><img class="preview" id="preview2"/></td>
                </tr>
                <tr>
                    <td><input type="file" accept="image/*" name="img2file" id="img2file" required /></td>
                </tr>
                <tr>
                    <td><input id="phone" class="text" type="text" placeholder="电话" required /></td>
                </tr>
                <tr>
                    <td><span>预约日期:</span><input id="date" class="date" type="date" required /></td>
                </tr>
                <tr>
                    <td><input id="ETA" class="text" type="text" placeholder="预期到达时间(hh:mm)" required pattern="^(?:(?:[0-2][0-3])|(?:[0-1][0-9])):[0-5][0-9]$" /></td>
                </tr>
                <tr>
                    <td><textarea id="intention" placeholder="进出事由" required></textarea></td>
                </tr>
                <tr>
                    <td><textarea id="relevants" placeholder="涉及设备/系统" required></textarea></td>
                </tr>
            </table>

            <br />
            <div>
                <input class="button" type="submit" value="提交" />
                &emsp;&emsp;&emsp;&emsp;&emsp;
                <input class="button" type="reset" value="重置" />
            </div>
        </form>
    </div>
    <br /><br />
</body>

</html>

CSS

body {
    font-family: 'Microsoft YaHei';
    text-align: center;
}
table {
    margin: 0 auto;
    margin-top: 3%;
    text-align: center;
    font-size: 18px;
    width: 15%;
}
td {
    padding: 20px 0px 20px 0px;
}
input {
    font-size: 18px;
    font-family: 'Microsoft YaHei';
}
input.text {
    width: 300px;
    height: 40px;
}
input.date {
    width: 210px;
    height: 35px;
}
input.button {
    width: 80px;
    height: 40px;
}
.imgtext {
    text-align: left;
}
.preview {
    width: 300px;
    height: 200px;
    overflow: hidden;
}
#img1file, #img2file {
    font-size: 15px;
}
textarea {
    font-size: 18px;
    font-family: 'Microsoft YaHei';
    width: 300px;
    height: 100px;
    resize: none;
}

Javascript

用fileDom获取文件,自动判断是否为合法的文件类型,并将其显示到界面上的img标签中以供用户预览。

发送时需要先把用户的个人信息发过去,调用一次send()方法,再把两张图片分别发过去,每张图片用FileReader的readAsDataURL方法读取为base64code格式,再把这个字符串发到服务端。

// javascript
var start = function () {

    // 上传并预览图片
    var fileDom1 = document.getElementById("img1file");
    var previewDom1 = document.getElementById("preview1");
    var fileDom2 = document.getElementById("img2file");
    var previewDom2 = document.getElementById("preview2");

    fileDom1.addEventListener("change", e => {
        var file = fileDom1.files[0];
        // 检查上传的文件是否为图片格式
        if (!file || file.type.indexOf("image/") < 0) {
            fileDom1.value = "";
            previewDom1.src = "";
            return;
        }

        var fileReader = new FileReader();
        fileReader.onload = e => {
            previewDom1.src = e.target.result;
        };
        fileReader.readAsDataURL(file);
    });

    fileDom2.addEventListener("change", e => {
        var file = fileDom2.files[0];
        if (!file || file.type.indexOf("image/") < 0) {
            fileDom2.value = "";
            previewDom2.src = "";
            return;
        }

        var fileReader = new FileReader();
        fileReader.onload = e => {
            previewDom2.src = e.target.result;
        };
        fileReader.readAsDataURL(file);
    });

    // 建立websocket通信
    var wsImpl = window.WebSocket || window.MozWebSocket;
    window.ws = new wsImpl('ws://192.168.204.43:7181/');

    var form = document.getElementById('sendForm');
    var staff = document.getElementById('staff');
    var name = document.getElementById('name');
    var company = document.getElementById('company');
    var idcard = document.getElementById('idcard');
    var phone = document.getElementById('phone');
    var date = document.getElementById('date');
    var ETA = document.getElementById('ETA');
    var intention = document.getElementById('intention');
    var relevants = document.getElementById('relevants');

    // 以base64格式发送图片文件
    function sendFile(fileDom) {
        var file = fileDom.files[0];
        var reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function (e) {
            var base64Code = reader.result;
            ws.send(base64Code);
        };
    }

    // 覆写提交事件
    form.addEventListener('submit', function (e) {
        e.preventDefault();
        var msg = staff.value + ',' + name.value + ',' + company.value + ',' + idcard.value + ',' + phone.value + ',' + date.value + ',' + ETA.value + ',' + intention.value + ',' + relevants.value;
        ws.send(msg);
        sendFile(fileDom1);
        sendFile(fileDom2);
        alert("Your application has been submitted.");
        form.reset();
        previewDom1.src = "";
        previewDom2.src = "";
    });
};

window.onload = start;

C# winform服务端

websocket服务端与客户端通信,通过收到数据的开头子串判断是收到了图片base64码还是文本信息。

// websocket实现与客户端通信
private void StartServer()
{
    FleckLog.Level = LogLevel.Debug;
    var server = new WebSocketServer("ws://0.0.0.0:7181");

    server.Start(socket =>
    {
        socket.OnOpen = () => { };
        socket.OnClose = () => { };
        // 收到信息后自动添加到数据库
        socket.OnMessage = message =>
        {
            if (!message.StartsWith("data:image/"))  // 收到的消息是个人信息
            {
                addfromClient(message);
            }
            else  // 收到的消息是身份证照片
            {
                saveidcardImage(message);
            }
        };
    });
}

收到个人信息后的处理详见前置模块,下文介绍收到图片后的处理。

base64码开头会有一些固定的数据信息,在复原图片时这些信息不能被识别,要提取出有用的信息后将其去除。

代码中的image_directory是指定的路径,current_register_idcard是当前用户的身份证号,由于有两张图片,因此要在图片名称上用_a和_b区别开。

        // 将客户端发来的身份证照片的base64码复原成图片,并存储到本地文件夹
        private void saveidcardImage(string msg)
        {
            // base64码预处理
            string hz = msg.Split(',')[0].Split(';')[0].Split('/')[1];
            string[] str = msg.Split(',');
            byte[] imageBytes = Convert.FromBase64String(str[1]);

            //读入MemoryStream对象
            MemoryStream memoryStream = new MemoryStream(imageBytes, 0, imageBytes.Length);
            memoryStream.Write(imageBytes, 0, imageBytes.Length);
            
            //  保存图片
            Image image = Image.FromStream(memoryStream);
            string filename = image_directory + current_register_idcard + "_a." + hz;

            if (!File.Exists(filename))
            {
                image.Save(filename);
            }
            else
            {
                filename = image_directory + current_register_idcard + "_b." + hz;
                image.Save(filename);
            }
        }

总结

websocket相比ajax更偏向于实时交互的信息,且安全性较高,经过测试一般的抓包工具无法从数据中提取有价值的信息;
在传输图片这里我遇到了不小的麻烦,因为C#的Fleck包本身并不支持传输文件和图片,网上也很少有用C#做这个功能的博文,转成base64码的处理方式是一个可以实现的途径;
如果需要限制上传图片大小,可以在js文件中的图片校验那里增加大小的校验。


功能扩展

来访人员登记系统(十二)动态增减html页面表格表单并获取表单值传递给C#后台

posted @ 2020-09-25 14:10  老鼠司令  阅读(645)  评论(0)    收藏  举报