【问题标题】:Best way to deal with potentially very large form creating/posting using jquery $.download使用 jquery $.download 处理可能非常大的表单创建/发布的最佳方法
【发布时间】:2011-05-12 15:35:08
【问题描述】:

我有一个流程如下:

  1. 用户执行复杂的搜索,该搜索通过 ajaxly 完成,返回一堆 id(可能是 1,可能是 10000)

  2. 一旦有了用户,他们就可以选择一些东西,然后下载一个文件(这是一份基于 ID 和他们选择的东西的报告)

为此,我使用了$.download 的高度修改版本

在这里看到:

jQuery.download = function (url, data, method, loadingHolderDivId) {
if (url && typeof data == 'object') {
    //for this version, data needs to be a json object.  
    //loop through the data object..
    $('#' + loadingHolderDivId).html($('#LoadingScreen').html());
    
    var theForm = $('<form></form>').attr('action', url).attr('method', method).attr('id', 'jqueryDownloadForm').attr('target', 'iframeX');

    $.each(data, function (propertyName, propertyVal) {
        if (propertyVal != null) {
            if (typeof propertyVal == 'object') {

                //HANDLE ARRAYS!
                for (var i = 0, len = propertyVal.length; i < len; ++i) {
                    theForm.append($("<input />").attr('type', 'hidden').attr('id', propertyName + i.toString).attr('name', propertyName).val(propertyVal[i]));
                }
            }
            else {
                theForm.append($("<input />").attr('type', 'hidden').attr('id', propertyName).attr('name', propertyName).val(propertyVal));
            }
        }
    });


    var iframeX;
    var downloadInterval;

    // remove old iframe if has
    $("#iframeX").remove();
    // create new iframe
    iframeX = $('<iframe src="javascript:false;" name="iframeX" id="iframeX"></iframe>').appendTo('body').hide();

    if ($.browser.msie) {
        downloadInterval = setInterval(function () {
            // if loading then readyState is “loading” else readyState is “interactive”
            if (iframeX && iframeX[0].readyState !== "loading") {
                $('#' + loadingHolderDivId).empty();
                clearInterval(downloadInterval);
            }
        }, 23);
    }
    else {
        iframeX.load(function () {
            $('#' + loadingHolderDivId).empty();
        });
    }


    theForm.appendTo('body').trigger('submit').remove();
    return false;
}
else {
    //they didn't fill in the params.  do nothing
}


};

基本上,它所做的是解析数据中的内容,并从中构建一个表单。这很好用,当没有很多 id 时。但是当有 8000 时,在 IE 中需要 5 或 10 秒,这并不奇怪,众所周知 IE 不擅长 dom 操作。

另一个问题是,在 IE 中。 $('#' + loadingHolderDivId).html($('#LoadingScreen').html()); 直到完成构建表单之后才会真正发生。我猜这是因为它需要一秒钟才能完成,在它完成之前它已经忙于构建表单了。

我以这种方式构建表单的原因是默认模型绑定器会很高兴并将我的表单绑定到一个可爱的模型中。 id 列表被绑定到 ilist(整数)

以下是控制器操作的示例:

Function ExportUsers(ByVal model As ExportUsersPostModel) As ActionResult

下面是模型的示例:

<Serializable()> _
Public Class ExportUsersPostModel
    Public Property FilterUserIds As IList(Of Integer) = New List(Of Integer)
    Public Property FilterColumnIds As IList(Of Integer) = New List(Of Integer)
    public property ShowThis as boolean
    public property OtherStuff as string = string.empty
    Public Property FormatId As Integer
End Class

所以实际的问题有两个:

  1. 如何让我的“加载”消息在它开始以极其缓慢的形式构建死亡之前显示出来?

  2. 如何加快表单构建,或者以一种不会很慢但仍能让模型绑定器满意的方式构建表单?

【问题讨论】:

  • 你为什么要创建一个表单元素而不是使用普通的 $.post() ? DOM 操作正在扼杀你的表现。 (您可以使用 $.post() 让模型绑定器满意)
  • 不能用ajax获取文件!
  • 您可以使用$.post,将数据存储在服务器上的临时文件中,返回一个密钥,然后使用该密钥导航到下载页面以获取文件。
  • 就加速现有代码而言,$("&lt;input /&gt;")document.createElement("input") 慢得多。 JQuery 并不总是解决方案。
  • re:使用 $.post,并不是一个真正的选择。但一个好主意。回复:使用 document.createElement。好电话,没想到。

标签: jquery asp.net-mvc performance internet-explorer download


【解决方案1】:

如果您能够将模型作为 JSON 传递,则可以创建一个自定义 ModelBinder 来处理将 JSON 映射到您的数据结构。我最近为无法自动映射的对象类型这样做了。 Json.Net 提供了一个名为 JObject 的类,它接受一个 JSON 字符串并将其映射到一个动态 C# 对象。然后,您可以将动态对象映射到您的强类型对象。

要创建自定义 ModelBinder,只需创建一个继承自 IModelBinder 的类并实现 BindModel 方法。这是我的实现的副本。您的显然会略有不同:

internal class FilterBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (controllerContext == null)
            throw new ArgumentNullException("controllerContext");
        if (bindingContext == null)
            throw new ArgumentNullException("bindingContext");

        if ((controllerContext.HttpContext.Request.Form.Count > 1 || (controllerContext.HttpContext.Request.Form.Count == 1 && !string.IsNullOrWhiteSpace(controllerContext.HttpContext.Request.Form.AllKeys[0]))) || (controllerContext.HttpContext.Request.QueryString.Count > 1 || (controllerContext.HttpContext.Request.QueryString.Count == 1 && !string.IsNullOrWhiteSpace(controllerContext.HttpContext.Request.QueryString.AllKeys[0]))))
        {
            ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            string value = val == null || string.IsNullOrEmpty(val.AttemptedValue) ? string.Empty : val.AttemptedValue;
            if (string.IsNullOrEmpty(value)) return null;
            dynamic obj = JObject.Parse(value);
            return new FilterSet(obj);
        }
        else
            return null;
    }
}

我有一堆检查来确保我得到的东西是有效的,你可能需要也可能不需要。然后,在获得 JObject 之后,我将它传递给执行映射的构造函数。

【讨论】:

  • 啊,有趣,所以你说,将所有数据作为 json 对象放入一个表单字段中,然后提交并使用此自定义模型绑定器将其转换为我的模型......当然!
  • 我正在努力实现这一点……我的项目在 vb.net 中。我想不通:FilterSet(obj);'什么是过滤器集?
  • 好吧,所以我最终没有使用它,但它引导我找到了最终解决方案,即将整数列表作为一个字符串字段发送,并在模型上具有只读属性将它们变成列表(整数)感谢您的想法!
  • 抱歉我的代码有歧义。 FilterSet 对象是我创建的自定义对象。它基本上是传回的 JSON 对象的静态类型版本。很高兴能帮助您找到解决问题的方法!
  • 哦,好的,这只是将 obj 转换为您的类型。太酷了。
【解决方案2】:

好吧,也许只有我一个人,但是 iFrame + 尝试 ajax 文件下载 + 每次查询 10+ 秒(可能)= cloog.也许这是我作为 UI 工程师的这些年专注于客户所看到的,但我遇到了一个真正的问题,就是让别人在我的一个应用程序上等待那么久。必须有更好的方法,我认为它可以在 UI 端进行改进。

简而言之,您需要进行大量搜索,深入了解细节,然后转储数据。因此,就用户模式而言,我将从查看实现每项任务的选项开始。

搜索很简单......向服务器发送一个模式,然后返回一个结果。这里没有什么神奇之处,尽管 Ajax 可能会是一个不错的选择。

现在,数据的“下钻”。由于您可能在这里处理大量数据,因此您需要一种方法来允许用户以有组织且有效的方式快速轻松地通过大量“东西”。对我来说,这是对网格的尖叫。我的偏好是DataTables。这给你带来的是效率、组织、分页,最重要的是用户能够轻松地与数据交互以挑选“东西”。第 1 步中的 ajax 查询将通过 ajax "pipelining" 填充 DataTables,也就是说,它会抓取用户想要查看的内容(例如 25 个结果),并提前抓取并可能稍微落后一点以加快界面速度。所有数据都可用,一次只抓取一个部分,从而加快查询速度。用户可以在界面上对数据进行排序、过滤、排序和限制,方便他们highlight to select data

现在,进入第 3 步,一个可下载的报告。为了让它变得非常简单,Datatables 有一个名为"TableTools" 的插件,它会自动输出您提供给用户的数据的 Excel、PDF、文本和可打印版本。这是几行代码和几张图像......配置大约需要 10 分钟。瞧,完成了。是的,很简单。

至于能够处理那么多记录,是的。我有一个应用程序,它通过标准 MySQL 数据库(非集群)以这种方式处理了 250 万条记录因为你一次只抓取一个“切片”数据,它永远不必做任何奇异的、巨大的查询除了创纪录的数量。用户可以通过偶尔的微小延迟对结果进行翻页。这真是一件美丽的事情。

【讨论】:

  • 用户搜索速度快!这不是问题,他们得到了他们想要报告 ajaxly 的用户,它在
  • @Patricia,给这只猫剥皮的方法有一百万种。我的观点是作为开发人员退后一步,思考一下 UI。谷歌分析通过“安排”用户可以获取的报告来处理类似的问题。这是一个很好的解决方案,因为它消除了对某事发生的“等待”,并给出了“我们正在工作,稍后回来”的期望,这通常只有几秒钟到一分钟。尝试一次,这确实是一种吸引人的模式,并且可以在用户端弥补需要一两秒以上的时间来完成的过程。您甚至可以在它准备好时发出警报。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-30
  • 2014-02-11
  • 2019-09-25
  • 1970-01-01
  • 1970-01-01
  • 2013-01-20
  • 2015-02-28
相关资源
最近更新 更多