[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>
結果
參考