【问题标题】:Export All from DataTables with Server Side processing?使用服务器端处理从数据表中导出全部?
【发布时间】:2016-12-27 18:19:32
【问题描述】:

我有使用 DataTables 服务器端处理的表格在我的网站上显示。我希望能够“全部导出”并导出所有行,而不仅仅是显示那些行。有 60000+ 行和 65+ 列,所以必须用服务器端处理。

我已经尝试了一些东西,但到目前为止没有任何效果。

我试过这个:

{ extend: 'excel',
    text: 'Export Current Page',
    exportOptions: {
        modifier: {
            page: 'current'
        }
    },
    customize: function (xlsx)
    {
        var sheet = xlsx.xl.worksheets['sheet1.xml'];
        $('row:first c', sheet).attr('s', '7');
    }
}

仅导出页面上显示的行。

我试过了:

{
    text: 'Export All to Excel',
    action: function (e, dt, button, config)
    {
        dt.one('preXhr', function (e, s, data)
        {
            data.length = -1;
        }).one('draw', function (e, settings, json, xhr)
        {
            var excelButtonConfig = $.fn.DataTable.ext.buttons.excelHtml5;
            var addOptions = { exportOptions: { 'columns': ':all'} };

            $.extend(true, excelButtonConfig, addOptions);
            excelButtonConfig.action(e, dt, button, excelButtonConfig);
        }).draw();
    }
}

这会将整个表格的数据发送到屏幕,而不是使用分页并将整个数据集发送到 excel 文件。

我在 Google 和 SO 中搜索过,但没有找到可行的解决方案。

我还应该提到,我想根据表上设置的当前过滤器全部导出。这样最终用户将只获得他们正在搜索的那些行的导出。他们通常将其限制为 30k - 40k 行,仍然有 65+ 列。我(还)不允许删除/隐藏列。

编辑/更新

这里有一个次要考虑:如果我不能从服务器的响应中导出全部,我可以在服务器上构建 Excel 文件吗?我的服务器没有安装 Excel,我仍然希望我的最终用户获取该文件。我确信我必须找到一种方法将 Excel 放到我的服务器上,但是我如何将任何创建的文件传输给最终用户,这甚至比仅仅发送整个数据集的响应并创建更快用户计算机上的 Excel 文件?

编辑

建议我尝试使用 jquery 的 $.ajax() 来使其正常工作。如果有人能给我一个想法,我会尝试第三个按钮。

我已经可以提取所有数据,使用用户添加的相同过滤器和排序,并使用按钮完成。上面的第二次尝试执行此操作,但将其发送到屏幕。我有 PHPExcel 和一个可以创建 Excel 工作表的文件。我将如何获取我在第二个按钮中获得的内容并将其发送到另一个文件以创建 Excel 工作表?我认为使用 jquery 的 $.ajax() 可能有效,我只是不知道如何获得它。我知道我必须使用$_POST,因为数据可能太大而无法使用$_GET 将数据发送到PHPExcel 文件。

我已经可以导出为 CSV,但我需要使用一些 CSV 没有的格式导出。这就是为什么我要麻烦使用 PHPExcel。

编辑 III

我正在尝试这个,虽然它还没有工作:

{
    text: 'Export all to Excel II',
    action: function (e, dt, button, config)
    {
        dt.one('preXhr', function (e, s, data)
        {
            data.length = -1;
        }).one('export', function (e, settings, json, xhr)
        {
            var excelButtonConfig = $.fn.DataTable.ext.buttons.excelHtml5;
            var addOptions = { exportOptions: { 'columns': ':all'} };

            $.extend(true, excelButtonConfig, addOptions);
            excelButtonConfig.action(e, dt, button, excelButtonConfig);
        })
    }
}

编辑 4

希望是最后一次编辑。

我知道我必须做三件事才能完成这项工作:

  1. 获取当前的排序和过滤
  2. 获取长度设置为 -1 的数据集
  3. 将此发送到 PHPExcel 文件以处理和创建 Excel 文件 我可以像这样创建一个按钮:

    { text: '将所有数据导出到 Excel', 行动: }

我只是不知道需要采取什么行动。

我在上面的第二次尝试提取了我需要的整个数据集,但将其发送到屏幕而不是我的 PHPExcel 文件 (ExportAllToExcel.php)。

我一直在努力解决这个问题,但还没有走多远。有人告诉我我需要使用$.ajax() 来执行此操作,有人告诉我我不需要使用它。我已经尝试过有和没有并且无法到达任何地方。

我也试过用这个没有效果:

$.fn.dataTable.ext.buttons.export =
{
    className: 'buttons-alert',
    "text": "Export All Test",
    action: function (e, dt, node, config)
    {
        var SearchData = dt.search();
        var OrderData = dt.order();
        alert("Test Data for Searching: " + SearchData);
        alert("Test Data for Ordering: " + OrderData);
    }
};

【问题讨论】:

  • 增加你在 php.ini 文件中的内存以解决你的错误。以及为什么要这样做或以这种方式导出
  • @ShaileshSingh 我过去曾尝试过。这不是一个可行的解决方案,因为没有足够的内存可以让它工作。无论我设置多大的限制,它总是会达到。
  • 我不再收到有关达到内存限制的错误。我认为它最初出现只是一个侥幸。
  • 不,您不能使用服务器端在客户端上导出,因为您没有所有行。在服务器上导出很简单,只需创建一个 CSV 文件,d'load 和 excel 就可以打开它
  • @ChrisCaviness 我不能使用 CVS,我需要在文件创建时添加格式,而 CVS 不允许这样做。此外,我能够在上面进行的第二次尝试中提取我需要的数据,我只是不知道如何将其发送到我拥有的文件中,该文件将使用 PHPExcel 创建 Excel 文件。

标签: javascript php jquery datatables-1.10


【解决方案1】:

首先在DataTable中添加如下代码

"dom": 'Blfrtip',
                    "buttons": [
                        {
                            "extend": 'excel',
                            "text": '<button class="btn"><i class="fa fa-file-excel-o" style="color: green;"></i>  Excel</button>',
                            "titleAttr": 'Excel',
                            "action": newexportaction
                        },
                    ],

然后在$(document).ready()函数里面加入这个函数

function newexportaction(e, dt, button, config) {
         var self = this;
         var oldStart = dt.settings()[0]._iDisplayStart;
         dt.one('preXhr', function (e, s, data) {
             // Just this once, load all data from the server...
             data.start = 0;
             data.length = 2147483647;
             dt.one('preDraw', function (e, settings) {
                 // Call the original action function
                 if (button[0].className.indexOf('buttons-copy') >= 0) {
                     $.fn.dataTable.ext.buttons.copyHtml5.action.call(self, e, dt, button, config);
                 } else if (button[0].className.indexOf('buttons-excel') >= 0) {
                     $.fn.dataTable.ext.buttons.excelHtml5.available(dt, config) ?
                         $.fn.dataTable.ext.buttons.excelHtml5.action.call(self, e, dt, button, config) :
                         $.fn.dataTable.ext.buttons.excelFlash.action.call(self, e, dt, button, config);
                 } else if (button[0].className.indexOf('buttons-csv') >= 0) {
                     $.fn.dataTable.ext.buttons.csvHtml5.available(dt, config) ?
                         $.fn.dataTable.ext.buttons.csvHtml5.action.call(self, e, dt, button, config) :
                         $.fn.dataTable.ext.buttons.csvFlash.action.call(self, e, dt, button, config);
                 } else if (button[0].className.indexOf('buttons-pdf') >= 0) {
                     $.fn.dataTable.ext.buttons.pdfHtml5.available(dt, config) ?
                         $.fn.dataTable.ext.buttons.pdfHtml5.action.call(self, e, dt, button, config) :
                         $.fn.dataTable.ext.buttons.pdfFlash.action.call(self, e, dt, button, config);
                 } else if (button[0].className.indexOf('buttons-print') >= 0) {
                     $.fn.dataTable.ext.buttons.print.action(e, dt, button, config);
                 }
                 dt.one('preXhr', function (e, s, data) {
                     // DataTables thinks the first item displayed is index 0, but we're not drawing that.
                     // Set the property to what it was before exporting.
                     settings._iDisplayStart = oldStart;
                     data.start = oldStart;
                 });
                 // Reload the grid with the original page. Otherwise, API functions like table.cell(this) don't work properly.
                 setTimeout(dt.ajax.reload, 0);
                 // Prevent rendering of the full data to the DOM
                 return false;
             });
         });
         // Requery the server with the new one-time export settings
         dt.ajax.reload();
     }

【讨论】:

    【解决方案2】:

    我有这个工作,主要是。它现在超时,但由于数据大小不适用于此工作,这是一个单独的问题。对于小型数据集,它可以完美运行。

    这就是我创建按钮的方式(这是我在这里使用的 export 按钮):

    "buttons": [{
                    extend: 'collection',
                    text: 'Selection',
                    buttons: ['selectAll', 'selectNone']
                }, {
                    extend: 'collection',
                    text: 'Export',
                    buttons: ['export', 'excel', 'csv', 'pdf', { extend: 'excel',
                        text: 'Export Current Page',
                        exportOptions: {
                            modifier: {
                                page: 'current'
                            }
                        },
                        customize: function (xlsx)
                        {
                            var sheet = xlsx.xl.worksheets['sheet1.xml'];
                            $('row:first c', sheet).attr('s', '7');
                        }
                    }]
                }
                ]
    

    这是上面创建的按钮的初始化:

    $.fn.dataTable.ext.buttons.export =
    {
        className: 'buttons-alert',
        id: 'ExportButton',
        text: "Export All Test III",
        action: function (e, dt, node, config)
        {
            var SearchData = dt.rows({ filter: 'applied' }).data();
            var SearchData1 = dt.search();
            console.log(SearchData);
            var OrderData = dt.order();
            console.log(SearchData1);
            var NumCol = SearchData[0].length;
            var NumRow = SearchData.length;
            var SearchData2 = [];
            for (j = 0; j < NumRow; j++)
            {
                var NewSearchData = SearchData[j];
                for (i = 0; i < NewSearchData.length; i++)
                {
                    NewSearchData[i] = NewSearchData[i].replace("<div class='Scrollable'>", "");
                    NewSearchData[i] = NewSearchData[i].replace("</div>", "");
                }
                SearchData2.push([NewSearchData]);
            }
    
            for (i = 0; i < SearchData2.length; i++)
            {
                for (j = 0; j < SearchData2[i].length; j++)
                {
                    SearchData2[i][j] = SearchData2[i][j].join('::');
                }
            }
            SearchData2 = SearchData2.join("%%");
            window.location.href = './ServerSide.php?ExportToExcel=Yes';
        }
    };
    

    这是ServerSide.php文件中获取数据并将其发送到服务器进行处理的部分:

    require('FilterSort.class.php');
    
    if (isset($_GET['ExportToExcel']) && $_GET['ExportToExcel'] == 'Yes')
    {
        $request = @unserialize($_COOKIE['KeepPost']);
        $DataReturn = json_encode(FilterSort::complex($request,$sqlConnect,$table,$primaryKey,$ColumnHeader));
        require './ExportAllToExcel.php';
    }
    else
    {
        echo json_encode(FilterSort::complex($request,$sqlConnect,$table,$primaryKey,$ColumnHeader));
    }
    

    这就是我设置用于保留搜索和排序条件的 cookie 的方式:

    if(isset($_POST['draw']))
    {
        $KeepPost = $_POST;    
        $KeepPost['length'] = -1;
        $PostKept = serialize($KeepPost);
        setcookie("KeepPost",$PostKept,time() + (60*60*24*7));
    }
    

    所有这些组合将正确的标准发送到 FilterSort.class.php,它应该处理标准并将数据集返回到 ExportAllToExcell.php,然后创建 Excel 文件。现在我正在向它发送大量报告,但它会超时。

    更新

    我稍微改变了这样做的方式:

    这是一组新的按钮:

    "buttons": [{
        extend: 'collection',
        text: 'Export',
        buttons: ['export', { extend: 'csv',
            text: 'Export All To CSV',              //Export all to CSV file
            action: function (e, dt, node, config)
            {
                window.location.href = './ServerSide.php?ExportToCSV=Yes';
            }
        }, 'csv', 'pdf', { extend: 'excel',
            text: 'Export Current Page',            //Export to Excel only the current page and highlight the first row as headers
            exportOptions: {
                modifier: {
                    page: 'current'
                }
            },
            customize: function (xlsx)
            {
                var sheet = xlsx.xl.worksheets['sheet1.xml'];
                $('row:first c', sheet).attr('s', '7');
            }
        }]
    }
    ]
    

    这是我如何创建全部导出到 Excel 按钮:

    $.fn.dataTable.ext.buttons.export =
    {
        className: 'buttons-alert',                         //Adds the "Export all to Excel" button
        id: 'ExportButton',
        text: "Export All To Excel",
        action: function (e, dt, node, config)
        {
            window.location.href = './ServerSide.php?ExportToExcel=Yes';
        }
    };
    

    这些现在将数据发送到我之前使用的同一 ServerSide.php 文件:

    require('FilterSort.class.php');
    if (isset($_GET['ExportToExcel']) && $_GET['ExportToExcel'] == 'Yes')
    {
        include 'Helper/LogReport.php';
        $GetSQL = "Select Value from PostKept where UserName = '" .$_COOKIE['UserName']. "'";
        $KeepResult = $conn->query($GetSQL);
        $KeepResults = $KeepResult->fetchALL(PDO::FETCH_ASSOC);
    
        $request = unserialize($KeepResults[0]['Value']);
    
        $DataReturn = json_encode(FilterSort::complex($request,$sqlConnect,$table,$primaryKey,$ColumnHeader,1));
        require './ExportAllToExcel.php';
    

    我还更改了保留查询的方式,现在我还保留了 Table NameUserName,如下所示:

    include 'DBConn.php';
    $KeepPost = $_POST;                                     //POST holds all the data for the search
    $KeepPost['length'] = -1;                               //-1 means pulling the whole table
    $PostKept = serialize($KeepPost);                       //This takes the array of data and turns it into a string for storage in SQL
    $SQLCheck = "select distinct UserName from PostKept";   //Gets all the distinct Usernames of users that have used the Report Dashboard.
    $sth = $conn->query($SQLCheck);
    $CheckedUser = $sth->fetchALL(PDO::FETCH_ASSOC);
    foreach($CheckedUser as $User)
    {
        foreach($User as $Index => $Who)
        {
            $FoundUsers[] = $Who;                           //Taking all the found users and placing them into a simpler array for searching later
    
        }
    }
    
    if(isset($_COOKIE['UserName']) && in_array($_COOKIE['UserName'],$FoundUsers))   //If the user already has an entry update it with new information
    {
        $TSQL = "UPDATE PostKept set Value = '" .$PostKept. "', TableName = '" .$TableName. "' where UserName = '" .$_COOKIE['UserName']. "'";
    }
    else
    {
        if(isset($_COOKIE['UserName']))     //If this is a new user
        {
            $TSQL = "INSERT into PostKept(Value, TableName, UserName) select '" .$PostKept. "','" .$TableName. "','" .$_COOKIE['UserName']. "'";
        }
        else        //If this is on the Prod site and the User info is not yet kept
        {
            $TSQL = "INSERT into PostKept(Value, TableName) select '" .$PostKept. "','" .$TableName. "'";
        }
    }
    
    $sth = $conn->prepare($TSQL);
    $sth->execute();
    

    这就是现在所有组合将数据发送到我拥有的 ExportAllToExcel.php 文件,然后它反过来创建文件。

    【讨论】:

      【解决方案3】:

      我刚刚遇到了这个问题并想出了一个替代解决方案。

      在 DataTable 选项中,添加以下内容:

      "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
      

      这将允许用户选择所有行并将 -1 在“长度”查询字符串参数中发送到服务器。在服务器端,您需要处理负数并允许在接收到 -1 时返回所有行。

      这将显示表中的所有行并导出所有行。

      我知道这可能不适合 50-60K 行,但对于较小的数据集,无需在服务器端和客户端都实现任何额外代码即可工作。

      【讨论】:

      • 您是正确的,这适用于小型数据集,但是,这个特定的数据集可能超过 100K 行,因此将其打印到屏幕上需要 >5 分钟,这是不可接受的。我已经在菜单中有这个,因为它允许用户选择他们看到的内容。当行数少于 1K 时,它似乎运行良好,但是我每行有接近 200 列,因此它可能适用于行数较高但列数较少的较小数据集。这也不能回答我的问题,即从第一页打印所有内容,并且不需要显示所有内容即可打印所有内容。
      • 好的,迈克。明白你的意思了。我刚刚发布了答案,以防有人最终来到这里并遇到同样的问题,行数/列数较少。
      【解决方案4】:

      在按钮中:

      action: function (e, dt, node, config) {
      
      var formData = 'yourfilters';
      formData.begin = '0';
      formData.length = 'yourTotalSize';
      
      $http({
          url: 'yourURL',
          method: 'POST',
          data: JSON.stringify(formData)
      }).then(function (ajaxReturnedData) {
      
          dt.rows.add(ajaxReturnedData.data).draw();
          $.fn.dataTable.ext.buttons.excelHtml5.action.call(this, e, dt, node, config);
      
      });}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多