如何使用 Spring 3 MVC 实现进度条?

13

有没有人能够教我或者提供一个可行的例子来满足这个要求。

场景:

  1. 我的Web应用使用Spring MVC。
  2. 它提供的一个服务是,当用户点击一个按钮时,服务器将执行一个长时间运行的进程。(查询数据库,写文件,写日志等...)此过程可能需要几秒钟或几分钟。
  3. *问题*** 如何实现服务以更新客户端的进度。


  4. 服务返回true或false,表示该进程是否成功。

感谢您的回复。一段代码片段或完整的教程将非常有帮助。


2
有什么想法吗?我真的需要帮助。 - Jefrey Valencia
3
有没有人遇到过和我一样的问题:{ - Jefrey Valencia
2个回答

5
这里是一个可能的解决方案,用于解决进度条问题: task.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<html>
    <head>

        <script src="../js/jquery.min.js"></script>

        <script>
          $(document).ready(function () {
            $.getJSON(window.location.href.concat('/status'), function(data) {
              if (data === "created") {

              } else {
                // task is already being executed
                refreshProgress();
              }
            });
          });

          var width = 0;

          function getProgress() {
            $.getJSON(window.location.href.concat('/progress'), function(percentage) {
              $('#progressBar').css('width', percentage+'%');
              document.getElementById("label").innerHTML = percentage * 1 + '%';
              width = percentage;
            });
          }

          function start() {
            $.ajax({
              type: "post",
              data: $('#task').serialize(),
              success: function(data) {
                $('#progressBar').css('width', 100+'%');
                document.getElementById("label").innerHTML = 100 * 1 + '%';

                // do sth with the data after finished task
              }
            });

            width = 0;
            $('#progressBar').css('width', 0+'%');
            document.getElementById("label").innerHTML = 0 * 1 + '%';

            refreshProgress();
          }

          function refreshProgress() {
            $("#btnStart").prop("disabled",true);

            var id = setInterval(frame, 1000);
            function frame() {
                if (width >= 100) {
                    clearInterval(id);
                    $("#btnStart").prop("disabled",false);

                } else {
                    getProgress();
                }
            }
          }

        </script>

    </head>
    <body>

      <div class="container">

        <h2 class="text-center">Progress Bar Example</h2>
        <div class="progress">
          <div id="progressBar" class="progress-bar" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" style="width:0%">
            <div id="label">0%</div>
          </div>
        </div>

        <form:form method="POST" commandName="task" cssClass="form-horizontal">
        <fieldset>

        <div class="form-group">
          <label class="col-md-4 control-label" for="btnStart">Actions</label>
          <div class="col-md-8">
            <button id="btnStart" name="btnStart" class="btn btn-success">Start</button>
            <button id="btnStop" name="btnStop" class="btn btn-danger">Stop</button>
          </div>
        </div>

        </fieldset>
        </form:form>

      </div>

      <script>
        $('#task').submit(function () {
         start();
         return false;
        });
      </script>

    </body>
</html>

TaskController.java

@Controller
@RequestMapping(value = "/task")
public class TaskController {

    private Task task;

    @RequestMapping("")
    protected ModelAndView page() {
        ModelAndView model = new ModelAndView(VIEW_DIR + "task");

        if (this.task == null) {
            this.task = new Task();
        }

        model.addObject("task", this.task);

        return model;
    }

    @RequestMapping(value = "/status", method = GET)
    public @ResponseBody
    String getStatus() {

        return task.getStatus();
    }

    @RequestMapping(value = "/progress", method = GET)
    public @ResponseBody
    int getProgress() {

        return task.getProgress();
    }

    public ModelAndView form(@ModelAttribute Task task) {

        this.task = task;

        ModelAndView model = new ModelAndView(VIEW_DIR + "task");

        task.execute();

        model.addObject("task", this.task);

        return model;
    }

}

Task.java

public class Task {

    private int total;
    private int progress;
    private String status;

    public Task() {
        this.status = "created";

        // TODO get total here or pass via form
    }

    public void execute() {

        status = "executing";

        int i = 0;

        while (i < total && status.equals("executing")) {

            progress = (100 * (i + 1) / total);

            i++;
        }
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    public int getProgress() {
        return progress;
    }

    public void setProgress(int progress) {
        this.progress = progress;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
}

4
将任务作为控制器的私有字段存储是不线程安全的,也允许 Web 应用程序的任何访问者看到任务。这通常是不希望的。 - Adriaan Koster
不要这样做!不要在控制器中使用私有字段来存储用户状态。在这种情况下,你可以将状态存储在会话中。 - Yan Khonski

4
有很多种方法可以处理这种情况。其中一种方法是将工作建模为一个“进程”,其中包含一个“状态”,包括百分比完成情况。
如果你想象一下这在网站上可能会是什么样子,点击开始进程的按钮会提交一个表单,开始进程并为进程分配某种标识符,就像创建任何其他类型的对象一样。然后它会重定向到“进程状态”页面。
进程状态页面将查询进程的状态并显示它。它可能会有一个URL参数来表示进程的ID。也许它会使用AJAX调用返回进度百分比来更新自己。
在后端,现在需要解决几个问题:找出进程N的当前状态,并更新进程N的状态。你可以通过多种方式来完成这个任务,包括将进度存储在数据库中或者拥有一些正在运行的任务的内存表。你也可以使用某种启发式方法来估算百分比。例如,如果它是一个“注册新用户”的任务,那么如果用户表中有电子邮件地址,那么完成20%,如果用户头像表中有这个用户的数据,则完成40%等等。我不太推荐这种方法。

1
你好,感谢回复。你在帖子中提到有多种处理此问题的方法,但只列出了一种。你能详细说明其他解决方案吗?如果我采用这种方法,目前需要找到一种存储进度信息的方式。再次感谢您的回复。 - Jefrey Valencia
为了确定进度,您需要做以下几件事情之一。您需要一种在用户请求更新时计算进度的方法,或者您需要一种存储进度的方法,以便在用户请求更新时您可以获得它,或者您需要一种向用户推送消息的方法,告诉他们您已经取得了更多的进展。将数据推送到客户端本身就是一个相当复杂的主题,但这将使您不必费心存储或计算进度。 - Brandon Yarbrough

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