WebSocket应用实例--进度条实现

文章来源

应用背景:


系统新增资料 批量上传 功能,用户再上传过程中,不知道上传进度,等待时间过长会误以为系统导入异常 而关闭页面,重新导入,可能出现资料重复的问题。为此,需要提供类似文件上传进度 将执行进度回复给前端页面的方案。

消息从服务端客户端主动推送到客户端,最直接的方案是使用WebSocke(可以双向通信就行)。感觉是有点大材小用了。

在这里插入图片描述

在这里插入图片描述

具体实现

a,页面加载时与WebSocket服务端建立连接

b,服务端接收连接请求并记录下客户端socket连接

c,执行导入

d,服务端将进度返回前端(当前记录行/总记录行数)

e,前端解析

 

1,前端:

<script src="~/Scripts/jquery-1.6.4.js"></script>
<script type="text/javascript">
    var ws;
    $(
        function () {
            debugger;
            ws = new WebSocket("ws://" + window.location.hostname + ":" + window.location.port + "/plmimport/Home/WSChat?userid=" + $('#userid').val());
            ws.onopen = function () {
                //$("#messageSpan").text("Connected!");
                console.log("Connected!");
            };
            ws.onmessage = function (result) {
                //$("#messageSpan").text(result.data);
                $("#process").val(result.data.split('/')[0]);
                $("#process")[0].setAttribute("max", result.data.split('/')[1]);
                $("#processText").text(result.data);
                console.log(result.data);
            };
            ws.onerror = function (error) {
                //$("#messageSpan").text(error.data);
                console.log(error.data);
            };
            ws.onclose = function () {
                //$("#messageSpan").text("Disconnected!");
                console.log("Disconnected!");
            };
            
            $("#btnImport").click(function () {
                $(this).slideUp("slow");
                $("#process").show("slow");//.toggle("hidden");
                $("#p1").text("正在执行,请勿关闭当前页面!");
                $.ajax({
                    url: '/plmimport/home/index',
                    type: 'POST',
                    cache: false,
                    data: new FormData($('#uploadForm')[0]),
                    processData: false,
                    contentType: false
                }).done(function (res) {
                    $("#p1").text(res);
                    $("#btnImport").show("slow");
                }).fail(function (res) {
                    $("#p1").text(res);
                });
            });
        }
    );
</script>
<body>
    <div style="display:none;border: 1px solid #C8DBD3; padding: 8px; line-height: 30px; height:55px; background:url(../../Images/bg_odv.png);">
    </div>
    <input type="text" id="userid" value="@ViewBag.UserId" hidden />
    @*@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data", id = "form1" }))
    {*@
        <form id="uploadForm" enctype="multipart/form-data">
            <h2>料件导入(Excel)</h2>
            <span style="color:red">@ViewBag.PartTemplateInfo</span>
            <div>
                <fieldset id="myfieldset">
                    <legend>帮助 </legend>
                    <p style="color: Red; text-align: left;">
                        @Html.ActionLink("模版下载", "GetFile", new { down = "template" })
                    </p>
                </fieldset>
            </div>
            <div style="margin-top: 20px;">
                <fieldset id="myfieldset2">
                    <legend>导入信息提示区</legend>
                    @*<p id="" style="color: Red; text-align:left;">@ViewBag.error</p>*@
                    <div>
                        <progress id="process" value="1" max="100" hidden></progress>
                        <label id="processText"></label>
                    </div>
                    <p id="p1" style="color: Red; text-align:left;">@ViewBag.error</p>
                </fieldset>
            </div>
            <div style="margin-top: 20px;">
                <fieldset id="myfieldset1">
                    <legend>导入操作</legend>
                    <p>选择文件:<input id="FileUpload" type="file" name="files" style="width: 250px; height: 28px;background: White" class="easyui-validatebox" /></p>
                    <p>
                        <input id="btnImport" type="button" value="执行导入" style="width: 100px; height: 28px;" />
                    </p>
                </fieldset>
            </div>
        </form>
            @* } *@
    </body>

2,后段实现:

#region 使用WebSocket实现消息推送
private static Dictionary<string, System.Net.WebSockets.WebSocket> CONN_POOL = new Dictionary<string, System.Net.WebSockets.WebSocket>();
public void WSChat()
        {
            if (HttpContext.IsWebSocketRequest)
            {
                HttpContext.AcceptWebSocketRequest(ProcessWSChat);
            }
        }
async System.Threading.Tasks.Task ProcessWSChat(System.Web.WebSockets.AspNetWebSocketContext context)
        {
            string user = context.QueryString["userid"]?.ToString();
            if (string.IsNullOrEmpty(user))
            {
                return;
            }
            System.Net.WebSockets.WebSocket ws = context.WebSocket;

            if (CONN_POOL.Keys.Contains(user))
            {
                if (CONN_POOL[user] != ws)
                {
                    CONN_POOL[user] = ws;
                }
            }
            else
            {
                CONN_POOL.Add(user, ws);
            }
            while (true)
            {
                if (ws.State == System.Net.WebSockets.WebSocketState.Open)
                {
                    ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
                    System.Net.WebSockets.WebSocketReceiveResult result = await ws.ReceiveAsync(buffer, System.Threading.CancellationToken.None);
                    try
                    {
                        #region 连接关闭,删除连接池
                        if (ws.State != System.Net.WebSockets.WebSocketState.Open)
                        {
                            if (CONN_POOL.Keys.Contains(user))
                            {
                                CONN_POOL.Remove(user);
                            }
                            break;
                        }
                        #endregion
                    }
                    catch (Exception ex)
                    {
                        if (CONN_POOL.Keys.Contains(user))
                        {
                            CONN_POOL.Remove(user);
                        }
                    }
                }
                else
                {
                    //发送消息时已离线。退出
                    break;
                }

            }
        }

async private void SendMessageToClient(string message)
        {
            ///消息推送异常不抛错误。
            try
            {
                if (Session["plmUserid"] == null || !CONN_POOL.Keys.Contains(Session["plmUserid"].ToString()))
                {
                    return;
                }
                if (CONN_POOL.Keys.Contains(Session["plmUserid"].ToString()))
                {
                    if (CONN_POOL[Session["plmUserid"].ToString()].State == System.Net.WebSockets.WebSocketState.Open)
                    {
                        await CONN_POOL[Session["plmUserid"].ToString()].SendAsync(GetArrSegFromStr(message), System.Net.WebSockets.WebSocketMessageType.Text, true, System.Threading.CancellationToken.None);
                    }
                }
            }
            catch (Exception ex)
            {
            }
        }

/// <summary>
/// 字符串转ArraySegment<byte>,用于WebSocket发送消息
/// </summary>
/// <param name="text">要转换的文本</param>
/// <returns></returns>
ArraySegment<byte> GetArrSegFromStr(string text)
        {
            return new ArraySegment<byte>(System.Text.Encoding.UTF8.GetBytes(text));
        }
/// <summary>
/// ArraySegment<byte>转字符串,用于消息队列存储
/// </summary>
/// <param name="text">要转换的ArraySegment对象</param>
/// <returns></returns>
string GetStrFromArrSeg(ArraySegment<byte> arrSeg)
        {
            return System.Text.Encoding.UTF8.GetString(arrSeg.Array);
        }
#endregion

[HttpPost]
public async System.Threading.Tasks.Task<string> Index(HttpPostedFileBase filebase)
{
    SendMessageToClient("10/50");
    System.Threading.Thread.Sleep(1000);
    SendMessageToClient("20/50");
    System.Threading.Thread.Sleep(1000);
    SendMessageToClient("30/50");
    System.Threading.Thread.Sleep(1000);
    System.Threading.Thread.Sleep(1000);
    SendMessageToClient("40/50");
    System.Threading.Thread.Sleep(1000);
    SendMessageToClient("50/50");
    System.Threading.Thread.Sleep(1000);
    return null;
}

遇到的问题:

在使用Form 对button是submit类型,提交到Action时,页面点击 [执行导入]时,Firefox存在一个问题:载入页面时与 ws: 的连接中断。官方也一直没有解决此问题。Chrome就可以正常执行。

解决办法就是使用Ajax,如此页面URL就不会是等待加载 的状态了,数据也能提交执行,然后通过WebSocket将进度传回前端。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值