[C#]使用SignalR 實作檔案上傳進度條

  • 7783
  • 0
  • C#
  • 2016-02-19

[C#]使用SignalR 實作檔案上傳進度條

上一篇使用 SignalR 並實作push體驗到Server及時回報client 快感,

接下來我將先實戰檔案上傳進度條,

由於我公司是以IE9為主,所以我沒辦法利用Html5 File API輕鬆解決,

但透過SignalR 也不會花費我太多時間。

 

1.新增 ConnectionMapping class

public class ConnectionMapping<T>
    {
        private readonly Dictionary<T, HashSet<string>> _connections =
            new Dictionary<T, HashSet<string>>();

        public int Count
        {
            get
            {
                return _connections.Count;
            }
        }

        public void Add(T key, string connectionId)
        {
            lock (_connections)
            {
                HashSet<string> connections;
                if (!_connections.TryGetValue(key, out connections))
                {
                    connections = new HashSet<string>();
                    _connections.Add(key, connections);
                }

                lock (connections)
                {
                    connections.Add(connectionId);
                }
            }
        }

        public IEnumerable<string> GetConnections(T key)
        {
            HashSet<string> connections;
            if (_connections.TryGetValue(key, out connections))
            {
                return connections;
            }

            return Enumerable.Empty<string>();
        }

        public void Remove(T key, string connectionId)
        {
            lock (_connections)
            {
                HashSet<string> connections;
                if (!_connections.TryGetValue(key, out connections))
                {
                    return;
                }

                lock (connections)
                {
                    connections.Remove(connectionId);

                    if (connections.Count == 0)
                    {
                        _connections.Remove(key);
                    }
                }
            }
        }
    }

透過該類別才能正確對應相關使用者連線,畢竟 Web 是服務多人。

 

2.修改WorkHub class

private readonly static ConnectionMapping<string> _connections =
          new ConnectionMapping<string>();

 public override System.Threading.Tasks.Task OnConnected()
        {
            string name = Context.User.Identity.Name;
            _connections.Add(name, Context.ConnectionId);
            return base.OnConnected();
        }
        public override System.Threading.Tasks.Task OnDisconnected()
        {
            string name = Context.User.Identity.Name;
            _connections.Remove(name, Context.ConnectionId);
            return base.OnDisconnected();
        }
        public override System.Threading.Tasks.Task OnReconnected()
        {
            string name = Context.User.Identity.Name;

            if (!_connections.GetConnections(name).Contains(Context.ConnectionId))
            {
                _connections.Add(name, Context.ConnectionId);
            }
            return base.OnReconnected();
        }
 public void Activate()
        {
            Clients.Caller.newMessageReceived("開始上傳中...");          
        }

覆寫 OnConnected、OnDisconnected、OnReconnected(處理使用者連線)並新增 Activate method(開始訊息)

 

3.Controller

public ActionResult Uploader(HttpPostedFileBase AddFiles_file, string conid)
        {
            if (AddFiles_file != null && AddFiles_file.ContentLength > 0)
            {
                var fileName = Path.GetFileName(AddFiles_file.FileName);
                var path = Path.Combine(Server.MapPath("~/FileUploads"), fileName);
                //AddFiles_file.SaveAs(path);             
                byte[] buffer = new byte[4096];
                int readBytes = 0;
                int readedBytes = 0;
                long totalLen = AddFiles_file.ContentLength;
                //get workhub instance
                var hubContext = GlobalHost.ConnectionManager.GetHubContext<WorkHub>();
                //設定processbar最大值
                hubContext.Clients.Client(conid).setFilesize(totalLen);
                Action addmessage = () =>
                {
                    if (readBytes >= totalLen)
                        hubContext.Clients.Client(conid).sendMessage("檔案上傳完畢.", readedBytes);
                    else
                        hubContext.Clients.Client(conid).sendMessage(
                            string.Format("檔案上傳中..{0} / {1}", readBytes, totalLen)
                            , readedBytes);
                };
                var t0 = Task.Factory.StartNew(() =>
                {
                    using (FileStream wfs = new FileStream(path, FileMode.OpenOrCreate))
                    {
                        using (BinaryReader br = new BinaryReader(AddFiles_file.InputStream))
                        {
                            while (readBytes < totalLen)
                            {
                                readedBytes = br.Read(buffer, 0, buffer.Length);
                                wfs.Write(buffer, 0, readedBytes);
                                readBytes += readedBytes;
                                //使用SignalR push to client     
                                addmessage();                              
                            }
                        }
                    }
                });
                t0.Wait();
                if (t0.IsCompleted && !t0.IsFaulted)
                    return Json(new { success = true, message = "上傳成功" }, "text/html");
                else
                    return Json(new { success = false, message = "上傳失敗" }, "text/html");
            }
            else
                return Json(new { success = false, message = "上傳失敗" }, "text/html");
        }

 

4.Html

<script type="text/javascript">
    var hubconid;
    var tagHub;  
    $(function () {       
        $("#progressbar").progressbar({ value: 0 });

        tagHub = $.connection.longwork;

        tagHub.client.sendMessage = function (message, curstep) {
            UpdateProgress(message, curstep);
        };
        tagHub.client.newMessageReceived = function (message) {
            var result = $("#result");
            result.html(message);
        }
        tagHub.client.setFilesize = function (maxsize) {
            $("#progressbar").progressbar("option", "max", maxsize);
        }
        $.connection.hub.start(function () {
            hubconid = $.connection.hub.id;
        });
    });  
    function UpdateProgress(message,curstep) {      
        var result = $("#result");
        // set message
        result.html(message);     
        // get progress bar
        var value = $("#progressbar").progressbar("option", "value");
        // update progress bar
        $("#progressbar").progressbar("value", value + curstep);
    }
    function myuploadb() {
        var butupload = $('#btnUploadAll');
        var filelist = $('#AddFiles_file');         
        $("#myForm").ajaxForm({
            iframe: true,
            dataType: "json",
            data: { conid: hubconid },
            beforeSubmit: function () {
                var fileContent = $.trim(filelist.val());
                if (fileContent.length == 0) {
                    alert('請選擇要上傳的檔案.', 'Wraning Message');
                    return false;
                }
                return true;
            },
            beforeSend: function () {
                butupload
                .prop('disabled', true)
                .text('上傳中...');
                tagHub.server.activate();
            },
            success: function (result) {
                $("#myForm").resetForm();
                butupload
                .prop('disabled', false)
                .text('上傳');
                if (result.success) {
                    alert('OK');
                }
                else {
                    alert(result.message)
                }
                return false;
            },
            error: function (xhr, textStatus, errorThrown) {
                $("#myForm").resetForm();
                butupload
                .prop('disabled', false)
                .text('上傳');
                alert('檔案上傳錯誤.' + errorThrown, 'Error Message');
                return false;
            }
        });
    }
   </script>

 

結果

image

 

image

image

 

 

參考

Mapping SignalR Users to Connections