显示进度条以展示表单提交的进度

7
这个问题还没有完全回答,欢迎您的贡献!

我想在提交一个大表单时显示一个简单的进度条。

这个表单包含十几个字段和一些文件上传字段,用户可以选择图片。然后,当他点击 Create 按钮时,数据和图片被提交,并在数据库中创建实体。(只需一次点击即可提交表单和图片)。
一切都很顺利,但我想在提交过程中显示一个进度条。

我找到了许多教程,解释如何显示进度条,但我没有找到任何人解释如何显示指示方法完成工作百分比的进度条,例如,我希望在提交过程中看到10%、25%等等..

所以,基本上,这就是我做的:(这是一个ASP.NET MVC3项目)

@model MyModel

@using (Html.BeginForm(null, null, FormMethod.Post, new { id = "target-form", enctype = "multipart/form-data" }))
{
    //some Html.TextBoxFor() and other @Html.DropDownListFor

    @Html.TextBoxFor(m => m.File, new { type = "file"})

    <input type="submit" value="Create" class="submitButton" />

    <div id="progressBar"></div>
}

一个基本的控制器

[HttpPost]
public ActionResult Create(MyModel model)
{
    if (ModelState.IsValid)
    {
        DALLayer dal = new DALLayer()
        dal.AddEntity(model);

        return RedirectToAction("Index", "Home");
    }

    return null;
}

是否可以将我的最后一个<div>转换为一个显示上传进度状态的progressBar


以下是我的要求:

  • 无插件(这是一个个人项目,我想自己学习如何完成)。
  • 跨浏览器兼容,支持IE8+(如果可能的话)
  • jQuery可行,但不使用Flash。


非常感谢!

更新1 这里是一个JSFIDDLE,我正在尝试调整此链接,但没有成功...如果你认为你能帮忙,欢迎联系我!


更新2 好的,我使用了acarlon的答案来通过XMLHttpRequest提交表单,并且数据已经被正确地提交到控制器。然而,进度条仍然没有出现!
我只是替换了传递给控制器的数据:

formData = new FormData(document.getElementById("target-form") );
xhr.open("POST", "@Url.Action("MyMethod", "MyController")", true );

尝试一些不同的标题,例如:

xhr.setRequestHeader("X-File-Name", $('#Files_0__File')[0].files[0].name);
xhr.setRequestHeader("X-File-Size", $('#Files_0__File')[0].files[0].size);
xhr.setRequestHeader("X-File-Type", $('#Files_0__File')[0].files[0].type);
//Also tried with "Content-Length" but it doesn't care about it.

这里硬编码确保它有良好的值。当我更加熟悉它时,我将在循环中执行它。

当我提交表单时,XMLHttpRequest发送以下字段:

readyState: 4
status: 0 <= 应该是200,对吧?

错误处理程序中,我有以下值:

loaded: 0
total: 883526
type: "error"

因此,数据已提交到我的控制器,但我无法显示这个可恶的进度条...


1
AlexB,您是否在寻找一种方法来测量进度条的进度,以时间(使用的时间%)或步骤(2/4个任务完成)为单位? - Dave Alperovich
我正在寻找一个进度条来测量时间进度(使用的时间百分比)。 - AlexB
这并不难,只需要使用一个计时器即可。但是这种方法可能不够准确。我怀疑大部分时间它会达到10-20-100%;而其他时候可能会达到100%,然后挂起几秒钟。考虑到这种范围(因为你无法猜测进程的真实执行时间),你认为这种影响是有利的吗? - Dave Alperovich
从数据库活动中获取%的工作量是一项巨大的任务。这将延长工作时间。我不确定这里是否有令人满意的答案。 - Dave Alperovich
1
嗯,你说得对,我可能误解了你的第一个问题。我想我会降低自己的雄心,并尝试追踪步骤进展。在这种情况下,我需要知道每个文件何时被正确上传以跟踪进度。知道我有一个按钮来保存实体并上传文件后,我怎么能轻松地跟踪这个进度呢?请参见我的 Update2 - AlexB
显示剩余5条评论
2个回答

4

您可以使用XMLHttpRequest接收上传文件时的更新。也有各种jquery类型的包装器来实现相同的功能。我将描述使用XMLHttpRequest的解决方案。 首先拦截表单提交。

$("#target-form").submit(function () {

接下来创建XHR请求:

 xhr = new XMLHttpRequest();

然后注册以便接收有关进度事件的通知:

xhr.upload.addEventListener( "progress", function ( evt )
{
    if( evt.lengthComputable )
    {
        var progressPercent = ( evt.loaded / evt.total ) * 100;
        showProgress( value );//your function.
    }
}, false );


//Some other events you will probably want to subscribe to
xhr.addEventListener( "load", function ()
{            
    fileUploadComplete( this.responseText );
}, false );

xhr.addEventListener( "error", function ( first, second, third )
{
    fileUploadComplete( "Error: Image format not supported." );
}, false );

xhr.addEventListener( "abort", function ()
{
    fileUploadComplete( "Error: Upload was cancelled. Please try again." );
}, false );

打开XHR并传入任何参数(id仅作为示例)

xhr.open( "post", '@Html.Raw( @Url.Action( "UploadImage", new { someId = id } ) )', true );

// Set appropriate headers                
xhr.setRequestHeader( "Content-Type", "multipart/form-data" );
xhr.setRequestHeader( "X-File-Name", name );

// Send the file
xhr.send( file );

然后在控制器中:

public ActionResult UploadImage( int someId, HttpPostedFileBase userFile  )
{
    ...    
}

您将在更新处理程序中收到更新。

长时间运行的服务器任务扩展

如果服务器上有一些长时间运行的任务(例如将数据写入数据库),则需要对服务器上的操作进行分块(以便您可以在每个块完成后提供更新),并实现代码来处理可以从javascript查询的长时间运行的服务。请参见此处。基本思路是在服务器端启动任务,并定期从Javascript检查进度。您可能希望有两个单独的进度条,一个用于上传,一个用于服务器端操作。这为用户提供了更多信息-他们将知道文件上传已完成,现在正在发生一些服务器端操作。


好的,我的悬赏将在1小时后到期,所以我必须奖励某人,而你的解决方案似乎是最好的。我现在无法尝试,所以我会在今天进行尝试,并在需要时向您询问一些精度问题。 - AlexB
好的,听起来不错。 - acarlon
@AlexB - 还有,看看这个解决方案,它与我上面发布的非常相似,但包含更多细节:http://buildstarted.com/2011/07/17/asp-net-mvc-3-file-uploads-using-the-fileapi/ - acarlon
谢谢你提供的额外链接,我会去看一下。阅读你的解决方案,我有一个问题:当我提交表单时,我需要提交我的图片和表单(一些文本框、下拉列表等)。如何通过XMLHttpRequest传递我的Model(包含图片和其他值的字段)?在你的示例中传递了一个id,但我无法传递一个MyModel(客户端不知道MyModel)。你可以在我第一篇帖子的末尾看到我的控制器。我需要修改它吗? - AlexB
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/48924/discussion-between-acarlon-and-alexb - acarlon
显示剩余4条评论

2
添加一个Ajax计时器控件,它与服务器检查进度并设置时间间隔(例如每2秒)。
在新线程上执行您的服务器任务。这将使您的回发立即返回,同时在后台执行您的长时间任务。启动Ajax计时器。
当长时间任务工作时,请让它更新包含%完成的会话变量。
当计时器回传到服务器时,从会话变量中获取%完成情况。
您可以通过拥有内部div和外部div来设计进度条。将内部div的宽度设置为完成的%:$('#progressBarWorkDone').css('width', WorkDone + '%')。
<div id="progressBar" style="width:400px;">    
<div id="progressBarWorkDone" style="width:0px;height:10px;background-color:red;"><div/> 
<div/>

请注意,如果您不知道如何使用计时器控件,您也可以使用 setInterval(function(){ AJAX CALLBACK GOES HERE },1000) 从 JavaScript 执行 AJAX 调用。
然而,说了这么多,最简单的方法是在进度条 div 中放置一个动画 gif。隐藏该 div,然后在单击提交按钮时显示该 div。
<input type="submit" onclick="$('#progressBar').show();" value="Create" class="submitButton" />

<div id="progressBar" style="display:none;"><img src="myProgressBar.gif" /></div>

计时器控件定期向服务器进行回发,获取完成工作的百分比。然后更新客户端浏览器。 - MarzSocks
你的更新答案真的很有趣。我更喜欢第一种解决方案,因为用户可以“实时”看到表单提交的进度。然而,我不确定如何实现这一点(抱歉,我是初学者)。当我的控制器中调用Create操作时,我是否需要创建一个新线程?如何定义完成工作的百分比?感谢您的帮助! - AlexB
我必须承认 - 实时显示进度并不是最容易的事情。特别是在没有插头的要求下 :-). 但是它是可以做到的。为了更好地帮助您,您能告诉我您有多少 JQuery AJAX 经验吗? - MarzSocks
在这种情况下,我建议您围绕AJAX回发做一些功课,因为这是实现您的目标而不使用插件所必需的。执行AJAX回发通常需要两个步骤。首先,服务器需要配置以接受回发。其次,客户端需要一些JavaScript来执行回发,jQuery可以做到这一点,因此对于客户端,请参见:https://api.jquery.com/jQuery.post/ 为了使服务器能够从客户端接受回发(RESTful方式),请参见:http://www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide - MarzSocks
我在项目中有很多使用$.ajax({ ... })的AJAX调用,因此我认为我已经掌握了它们的基础知识。请查看我的更新以查看我的进展。 - AlexB
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接