Asp.Net MVC页面显示后台处理进度问题

 这个问题的背景是,用户通过浏览器上传文件或Excel数据到系统中,页面需要时时显示后台处理进度,以增强用户的体验。

 在GitHub上找到一个一个项目,基本实现了这个功能,具体效果如下图

 

 代码实现过程大概如下:

 第一步 :定义一个HomeController类,用来处理浏览器的上传文件和进度情况

 

 1 public class HomeController : Controller
 2     {
 3         //
 4         // GET: /Home/
 5 
 6         public ActionResult Index()
 7         {
 8             return View();
 9         }
10 
11         public ActionResult Homepage()
12         {
13             return View();
14         }
15 
16         [HttpPost]
17         public ActionResult GetUniqueIdentifier()
18         {
19             return Json(Guid.NewGuid().ToString());
20         }
21 
22         [HttpPost]
23         public ActionResult SingleFileUpload()
24         {
25             return View();
26         }
27 
28         [HttpPost]
29         public ActionResult MultipleFileUpload()
30         {
31             return View();
32         }
33 
34         [HttpPost]
35         public ActionResult DoUploadSingleFile(HttpPostedFileBase berkas, string guid)
36         {
37             bool result = false;
38             string filePath = Server.MapPath("~/Temporary/") + berkas.FileName;
39 
40             int fileLength = berkas.ContentLength;
41             HttpContext.Cache[guid + "_total"] = fileLength;
42             byte[] fileContent = new byte[fileLength];
43             int bufferLength = 5 * 1024;
44             byte[] buffer = new byte[bufferLength];
45             int bytesRead = 0;
46 
47             FileStream outputFileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite); 
48             using (Stream inputFileStream = berkas.InputStream)
49             {
50                 while ((bytesRead = inputFileStream.Read(buffer, 0, buffer.Length)) > 0)
51                 {
52                     outputFileStream.Write(buffer, 0, bytesRead);
53                     outputFileStream.Flush();
54 
55                     HttpContext.Cache[guid + "_current"] = Convert.ToInt32(HttpContext.Cache[guid + "_current"]) + bytesRead;
56                     Debug.WriteLine(HttpContext.Cache[guid + "_current"].ToString());
57                     Thread.Sleep(50);
58                 }
59 
60                 inputFileStream.Close();
61                 inputFileStream.Dispose();
62             }
63 
64             outputFileStream.Close();
65             outputFileStream.Dispose();
66             result = true;
67 
68             return Json(result);
69         }
70 
71         [HttpPost]
72         public ActionResult TrackProgress(string guid)
73         {
74             try
75             {
76                 double paramCurrentFileSize = Convert.ToDouble(HttpContext.Cache[guid + "_current"]);
77                 double paramTotalFileSize = Convert.ToDouble(HttpContext.Cache[guid + "_total"]);
78                 int uploadProgress = Convert.ToInt32(paramCurrentFileSize * 100 / paramTotalFileSize);
79 
80                 return Json(uploadProgress);
81             }
82             catch (Exception)
83             {
84                 return Json(0);
85             }
86         }
87     }

DoUploadSingleFile方法用来将用户传来的文件保存到Temporary目录下,将文件的总大小和已经写入的文件数量分别放到Cache中,以便接下来读取这两个数据。

TrackProgress方法就是讲DoUploadSingleFile方法总保存的两个数读出并计算比例。

第二步,Web页面,主要存放三部分,主要是上传组件和进度显示,通过JS绑定了按钮和上传送事件。

@using X_Cust_File_Upload.Helpers

@{
    Layout = null;
    ViewBag.Title = "SingleFileUpload";
}


@*@PageHelper.Script("angular-js/js/angular.js", false)
@PageHelper.Script("underscore-js/js/underscore.js", false)*@
@PageHelper.Script("single-file-upload.js", true)

<div class="title">
    Single File Upload    
</div>

<div class="content">
    <form method="post" enctype="multipart/form-data" id="form_upload" name="form_upload" style="display: none;">
        <input type="file" name="berkas" id="berkas" />
    </form>

    <div class="control-group">
        <div class="input-prepend">
            <button class="btn" id="buttonSelectFile"><i class="icon icon-folder-open"></i></button>
            <input type="text" name="berkas_name" id="berkas_name" class="span3 uneditable-input" placeholder="Select File to Upload" />
        </div>

        <button class="btn btn-primary" id="buttonUploadFile"><i class="icon icon-upload"></i> Upload File</button>

        <div id="notification-area" class="alert info pull-right notification-area">    
        </div>
    </div>
</div>




<script type="text/javascript">
    $(document).ready(function () {
        $("#buttonSelectFile").on("click", function (event) {
            $("#berkas").trigger("click");
        });

        $("#berkas").on("change", function (event) {
            $("#berkas_name").val($("#berkas").val());
        });

        $("#buttonUploadFile").on("click", function (event) {
            var notificationArea = new NotificationArea($("#notification-area"));

            var fileUpload = new FileUpload();
            fileUpload.uploadSingleFile($("#form_upload"), "87shd-09ld2-9sdkl-09dlp-02kdm", "@Url.Content("~/Home/DoUploadSingleFile")", notificationArea, "@Url.Content("~/Home/TrackProgress")");
        });
    });
</script>

第三步,主要是ajax上传事件,即single-file-upload.js文件,在上传数据的时候,开启一个定时器,每个1s向TrackProgress方法发送一次请求,获取已经上传的进度。

function NotificationArea($container) {
    this.showProgressNotification = function ($progress, $isVisible) {
        $container.html("<span>Progress : " + $progress + " %</span>");

        if ($isVisible == false) {
            $container.fadeIn();
        }
    };

    this.showErrorNotification = function () {
        $container.removeAttr("class");
        $container.addClass("alert error pull-right");
        $container.html("<span>Upload error.</span>");
    };

    this.showSuccessNotification = function () {
        $container.removeAttr("class");
        $container.addClass("alert info pull-right");
        $container.html("<span>Uploaded successfully.</span>");
    };
}

function FileUpload() {
    this.guid = "";
    this.onUploadProgress = false;
    this.notificationObject = null;
    this.trackUrl = "";

    this.uploadSingleFile = function ($form, $guid, $url, $notificationObject, $trackUrl) {
        if ($form != null) {
            this.guid = $guid;
            //this.notificationObject = $notificationObject;
            this.trackUrl = $trackUrl;
            var trackTimer = setInterval(function () {
                trackUploadProgress($trackUrl, $notificationObject, $guid);
            }, 1000);

            $form.ajaxSubmit({
                url: $url,
                data: {
                    guid: $guid
                },
                beforeSend: function () {
                    $notificationObject.showProgressNotification(0, false);
                },
                success: function (data) {
                    console.log("sukses");

                    if (data == true) {
                        clearTimeout(trackTimer);
                        $notificationObject.showSuccessNotification();
                    }
                    else {
                        $notificationObject.showErrorNotification();
                    }
                },
                error: function (xhr, ajaxOptions, error) {
                    $notificationObject.showErrorNotification();
                },
                complete: function () {
                    clearTimeout(trackTimer);
                }
            });
        }
    };
}

function trackUploadProgress($url, $notificationObject, $guid) {
    console.log("Upload progress");

    $.ajax({
        url: $url,
        type: "post",
        data: {
            guid: $guid
        },
        success: function (data) {
            $notificationObject.showProgressNotification(data, true);
        }
    });
}

到此,主要功能已经完成,上传文件可以看到大致的进度。

但是我将这个功能集成到自己的系统时候遇见了一个很奇怪的问题:进度一直不提示,直到数据上传成功了,提示显示100%。这个问题的原因还不确定,请院子里的大牛帮忙分析下。

在原有的系统上增加权限验证功能,如果用户没有登录系统是不能上传数据的,但也就是这个功能,造成了上面的问题。我的做法如下:

第四步,增加AuthorizeAttribute认证子类,主要功能是用来判断用户是否登录系统,如果没有登录,跳转到登录页面,让用户登录。

public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }
            if (!httpContext.User.Identity.IsAuthenticated)//未登录的话 跳转到登录界面
                return false;
            return true; 
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.Result = new RedirectResult("/Auth/LogOn");
        }
    }

第五步,用户登录成功后,跳转到上传页面,这个工程使用ajax登录,JS代码如下:

$.ajax({
                type: "POST",
                url: "LogOn",
                data: { name: $("#UserName").val(), pwd: $("#Password").val(), vlidateCode: $.trim($("#ValidateCode").val())},
                dataType: "json",
                success: function (data) {                   
                    if(data.isSuccess)
                    {                       
                        window.location = data.url;
                    }
                    else
                    {
                        alert(data.message);
                        changeCheckCode();
                    }
                    $("#Loading").hide();
                }
            });

 

后台登录过程为AuthorController类,主要设置用户已经登录标志,

1 [HttpPost]
2         public JsonResult LogOn(string name, string pwd, string vlidateCode)
3         {
4                 FormsAuthentication.SetAuthCookie(name, false);
5                 var json = new { isSuccess = true, url = "../Home" };
6                 return Json(json);
7         }
View Code

 

第六步,在HomeController类上添加认证属性

  [CustomAuthorize(Roles="T1")]
    public class HomeController : Controller
    {
        ........
    }

 

原作者程序地址:http://files.cnblogs.com/files/crazyguo/x-cust-file-upload-master.zip

我增加后的程序:http://files.cnblogs.com/files/crazyguo/My.7z

  

 

posted on 2015-10-14 18:50  郭欢欢  阅读(4714)  评论(2编辑  收藏

导航