如何使用 Ajax 从数据表中导出所有行?

Posted

技术标签:

【中文标题】如何使用 Ajax 从数据表中导出所有行?【英文标题】:How to export all rows from Datatables using Ajax? 【发布时间】:2015-12-17 23:59:20 【问题描述】:

我正在使用数据表中的新功能:“html5 导出按钮”。我正在使用 Ajax 加载数据。

https://datatables.net/extensions/buttons/examples/html5/simple.html

问题是它只导出当前显示的页面。

我是这样导出的:

buttons: [
    
        extend: 'pdfHtml5',
        text: 'PDF',
        exportOptions: 
            "columns": ':visible',
        
    ,
]

如何导出所有行?

【问题讨论】:

您是否使用服务器端处理,即您的初始化选项中有serverSide: true 有 download 插件用于退役的 TableTools 扩展,允许在服务器端处理模式下生成 PDF。使用替换 TableTools 的新 Buttons 扩展,没有类似功能的文档。 【参考方案1】:

您需要告诉 AJAX 函数获取所有数据,然后执行导出但取消实际绘制,这样所有数据都不会加载到 DOM 中。不过,DataTables API 的完整数据仍将存在于内存中,因此您需要将其刷新为导出前的状态。

var oldExportAction = function (self, e, dt, button, config) 
    if (button[0].className.indexOf('buttons-excel') >= 0) 
        if ($.fn.dataTable.ext.buttons.excelHtml5.available(dt, config)) 
            $.fn.dataTable.ext.buttons.excelHtml5.action.call(self, e, dt, button, config);
        
        else 
            $.fn.dataTable.ext.buttons.excelFlash.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);
    
;

var newExportAction = function (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 
            oldExportAction(self, 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();
;

和:

    buttons: [
        
            extend: 'excel',
            action: newExportAction
        ,

【讨论】:

在我的情况下,此代码仅适用于 excel 按钮,其他按钮(如 pdf 或 csv)此代码不起作用 @Жасулан Бердибеков 更改.. if (button[0].className.indexOf('buttons-excel') >= 0) if (button[0].className.indexOf('buttons-excel') >= 0 || button[0].className.indexOf('buttons-csv') >= 0) 最佳答案!!设法做到这一点并仅在几秒钟内导出 30k 行,但你能告诉我如何导出为 pdf 吗?设法用 excel 和 csv 做到这一点,但是在尝试生成 pdf 时,pdf 文件已损坏 当您想导出所有数据同时在浏览器上只查看少量数据时,这很有用。谢谢! 优秀的解决方案,但我是 OOM 服务器端,有 48k 条记录。【参考方案2】:

非常感谢用户“kevinpo”。他给出了当服务器端处理开启时如何将 jquery 数据表中的所有记录下载为 excel 的方式。 根据他的回答,我在这里为服务器端处理实现了完整的导出功能(复制、excel、csv、pdf、打印)。

$(document).ready() 中定义以下函数并在每个导出按钮的action 上调用此函数,如下所示:

/* For Export Buttons available inside jquery-datatable "server side processing" - Start
- due to "server side processing" jquery datatble doesn't support all data to be exported
- below function makes the datatable to export all records when "server side processing" is on */

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();
;
//For Export Buttons available inside jquery-datatable "server side processing" - End

对于按钮,定义如下

"buttons": [
               "extend": 'copy',
               "text": '<i class="fa fa-files-o" style="color: green;"></i>',
               "titleAttr": 'Copy',                               
               "action": newexportaction
            ,
            
               "extend": 'excel',
               "text": '<i class="fa fa-file-excel-o" style="color: green;"></i>',
               "titleAttr": 'Excel',                               
               "action": newexportaction
            ,
            
               "extend": 'csv',
               "text": '<i class="fa fa-file-text-o" style="color: green;"></i>',
               "titleAttr": 'CSV',                               
               "action": newexportaction
            ,
            
               "extend": 'pdf',
               "text": '<i class="fa fa-file-pdf-o" style="color: green;"></i>',
               "titleAttr": 'PDF',                               
               "action": newexportaction
            ,
            
               "extend": 'print',
               "text": '<i class="fa fa-print" style="color: green;"></i>',
               "titleAttr": 'Print',                                
               "action": newexportaction
            ],

就是这样。现在您的下载已准备就绪。

【讨论】:

在其他高票答案失败后为我完美地工作......我真的需要用 js 变得更好 像魔术一样工作!多么了不起的功能……你应该得到更多的赞! 这个答案应该是投票最高的答案。这节省了我几个小时的工作时间。荣誉 一种改进是将 data.length 更改为数据表中的实际记录数,而不是一个大的随机数:data.length = $('#id').DataTable().page.info().recordsTotal; @Stoobish 你不需要DataTable id,你可以使用dt like data.length = dt.page.info().recordsTotal;【参考方案3】:

根据DataTables documentation,当您使用服务器端时,无法导出所有行:

关于服务器端处理的特别说明:在服务器端处理模式 (serverSide) 下使用 DataTables 时,selector-modifier 对所选行的影响很小,因为所有处理(排序、搜索等)都在服务器。因此,客户端唯一存在的行是表格中任何时候显示的行,选择器只能选择当前页面上的行。

我通过在长度菜单中添加一个“ALL”参数来解决这个问题,并培训最终用户在执行 PDF(或 XLS)导出之前显示所有记录:

var table = $('#example').DataTable(
    serverSide: true,
    ajax: "/your_ajax_url/",
    lengthMenu: [[25, 100, -1], [25, 100, "All"]],
    pageLength: 25,
    buttons: [
        
            extend: 'excel',
            text: '<span class="fa fa-file-excel-o"></span> Excel Export',
            exportOptions: 
                modifier: 
                    search: 'applied',
                    order: 'applied'
                
            
        
    ],
    // other options
);

【讨论】:

谢谢,但我已经从你的回答中弄清楚了。谢谢你,先生。 1+【参考方案4】:

是的,完全有可能做到这一点。 在内部,DataTables 有一个名为buttons.exportData() 的函数。当您按下按钮时,调用此函数并返回当前页面内容。 您可以覆盖该函数,以便它根据当前过滤器提取所有服务器端结果。并调用用于 ajax 分页的相同 url。

在初始化表之前覆盖它。代码如下:

$(document).ready(function() 

    jQuery.fn.DataTable.Api.register( 'buttons.exportData()', function ( options ) 
            if ( this.context.length ) 
                var jsonResult = $.ajax(
                    url: 'myServerSide.json?page=all',
                    data: search: $(#search).val(),
                    success: function (result) 
                        //Do nothing
                    ,
                    async: false
                );

                return body: jsonResult.responseJSON.data, header: $("#myTable thead tr th").map(function()  return this.innerHTML; ).get();
            
         );

    $("#myTable ").DataTable(
        
            "dom": 'lBrtip',
            "pageLength": 5, 
            "buttons": ['csv','print', 'excel', 'pdf'],
            "processing": true,
            "serverSide": true,
            "ajax": 
                "url": "myServerSide.json",
                "type": 'GET',
                "data": search: $(#search).val() 
            
        
);

【讨论】:

如果需要区分不同的DataTable对象,可以在函数内部使用this.table().node().id获取表id @RobertHume 我把这一行放在哪里 this.table().node().id【参考方案5】:

这个按钮定义在滚动表中对我有用(而不是分页):


  text: 'PDF',
  action: function(e, dt, button, config) 
    dt.one('preXhr', function(e, s, data) 
      data.length = -1;
    ).one('draw', function(e, settings, json, xhr) 
      var pdfButtonConfig = $.fn.DataTable.ext.buttons.pdfHtml5;
      var addOptions =  exportOptions:  "columns" : ":visible" ;

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

它将强制 DataTable 为一个请求请求当前过滤的所有行。然后它直接调用导出按钮所需的操作。变量addOptions可以用来改变导出按钮的标准配置。

如果你有很多行,你可能会遇到问题,因为它们都加载到 DOM 中。

【讨论】:

【参考方案6】:

我知道这是一个老问题,但是对于任何为此苦苦挣扎的人,这是我的解决方案。

变量:

var downloading = false,
    downloadTimestamp = null;

下载按钮定义:

buttons: [
    text: '<span class="glyphicon glyphicon-save-file" aria-hidden="true"></span>',
    titleAttr: 'CSV',
    className: 'downloadCSV',
    action: function(e, dt, node, config) 
        if (downloading === false)  //if download is in progress, do nothing, else
            node.attr('disabled', 'disabled'); //disable download button to prevent multi-click, probably some sort of *busy* indicator is a good idea

            downloading = true; //set downloading status to *true*

            dt.ajax.reload(); //re-run *DataTables* AJAX query with current filter and sort applied
        
    
]

Ajax 定义:

ajax: 
    url: ajaxURL,
    type: 'POST',
    data: function(data) 
        data.timestamp = new Date().getTime(); //add timestamp to data to be sent, it's going to be useful when retrieving produced file server-side

        downloadTimestamp = data.timestamp; //save timestamp in local variable for use with GET request when retrieving produced file client-side

        if (downloading === true)  //if download button was clicked
            data.download = true; //tell server to prepare data for download
            downloading = data.draw; //set which *DataTable* draw is actually a request to produce file for download
        

        return  data: JSON.stringify(data) ; //pass data to server for processing
    


'preDrawCallback'函数:

preDrawCallback: function(settings) 
    if (settings.iDraw === downloading)  //if returned *DataTable* draw matches file request draw value
        downloading = false; //set downloading flag to false

        $('.downloadCSV').removeAttr('disabled'); //enable download button

        window.location.href = ajaxURL + '?' + $.param( ts: downloadTimestamp ); //navigate to AJAX URL with timestamp as parameter to trigger file download. Or You can have hidden IFrame and set its *src* attribute to the address above.

        return false; //as it is file request, table should not be re-drawn
    


服务器端:

if(download == false),然后服务器执行 SELECT columns FROM tables WHERE rowNumber BETWEEN firstRow AND lastRow 并输出结果以在 DataTable。

if(download == true),然后服务器执行 SELECT columns FROM tables 并将所有行存储为 CSV 文件(或任何其他文件格式,具体取决于您的服务器环境能够产生)服务器端供以后通过 GET 请求检索。

以下是我在服务器端使用的 ASP JScript 代码:

    var timestamp = Number(Request.QueryString('ts')), //if it's a GET request, get timestamp
        tableData = 
            draw: data.draw,
            recordsTotal: 100, //some number static or dynamic
            recordsFiltered: 10, //some number static or dynamic
            data: []
        ;
        jsonData = String(Request.Form('data')), //if it's POST request, get data sent by *DataTable* AJAX
        data = jsonData === 'undefined' || jsonData.length === 0 ? null : JSON.parse(jsonData); //do some error checking (optional)

    if(!isNaN(timestamp))  //check timestamp is valid
        var csvTextKey = 'download-' + timestamp, //this is where timestamp value is used (can be any other unique value)
            csvText = Session(csvTextKey); //obtain saved CSV text from local server-side storage

        if(typeof csvText === 'undefined')  //if CSV text does not exist in local storage, return nothing (or throw error is You wish)
            Response.End();
        

        //if CSV exists:
        Response.ContentType = 'text/csv'; //set response mime type
        Response.AddHeader('Content-Disposition', 'attachment; filename=test.csv'); //add header to tell browser that content should be downloaded as file and not displayed

        Response.Write(csvText); //send all content to browser

        Response.End(); //stop further server-side code execution
    

    //if timestamp is not valid then we assume this is POST request, hence data should be either prepared for display or stored for file creation

    if(typeof data !== 'object' || data === null)  //do some more clever error checking
        throw 'data is not an object or is null';
    

        var recordset = data.download === true ? sqlConnection.Execute('SELECT * FROM #FinalTable') : Utilities.prepAndRunSQLQuery('SELECT * FROM #FinalTable WHERE rowId BETWEEN ? AND ?', [data.start, data.start + data.length], //execute SELECT either for display or for file creation
            headerRow = [],
            sqlHeaderRow = [],
            exportData = [];; 

        if(data.download === true)  //create CSV file (or any other file)
            if(!Array.isArray(data.columns)) 
                throw 'data.columns is not an array';
            

            for(var i = 0, dataColumnsCount = data.columns.length; i < dataColumnsCount; ++i) 
                var dataColumn = data.columns[i], //get columns data object sent by client
                    title = dataColumn.title, //this is custom property set on client-side (not shown in code above)
                    sqlColumnName = typeof dataColumn.data === 'string' ? dataColumn.data : (typeof dataColumn.data.display === 'string' ? dataColumn.data.display : dataColumn.data['_']); //set SQL table column name variable

                if(typeof title === 'string' && typeof sqlColumnName === 'string' && columnNames.indexOf(sqlColumnName) > -1)  //some more error checking
                    headerRow.push(title);
                    sqlHeaderRow.push(sqlColumnName);
                
            

            exportData.push('"' + headerRow.join('","') + '"'); //add table header row to in CSV file format
        

        while(recordset.EOF === false)  //iterate through recordset
            if(data.download === true)  //if download flag is set build string containing CSV content
                var row = [];

                for(var i = 0, count = sqlHeaderRow.length; i < count; ++i) 
                    row.push(String(recordset.Fields(sqlHeaderRow[i]).Value).replace('"', '""'));
                

                exportData.push('"' + row.join('","') + '"');
            

            else  //else format data for display
                var row = ;

                for(var i = 1, fieldsCount = recordset.Fields.Count; i < fieldsCount; ++i) 
                    var field = recordset.Fields(i),
                        name = field.Name,
                        value = field.Value;

                    row[name] = value;
                

                tableData.data.push(row);
            

            recordset.MoveNext();
        

if(data.download === true)  //save CSV content in server-side storage
    Session('download-' + data.timestamp) = exportData.join('\r\n'); //this is where timestamp value is used (can be any other unique value)


Response.Write(JSON.stringify(tableData)); //return data for display, if download flag is set, tableData.data = []

【讨论】:

【参考方案7】:

如果你使用 Laravel 框架,你可以使用这个....

$.fn.DataTable.Api.register( 'buttons.exportData()', function( options ) 
  if(this.context.length) 

    var src_keyword = $('.dataTables_filter input').val();

    // make columns for sorting
    var columns = [];
    $.each(this.context[0].aoColumns, function(key, value) 
      columns.push(
        'data' : value.data, 
        'name' : value.name, 
        'searchable' : value.bSearchable, 
        'orderable' : value.bSortable
      );
    );

    // make option for sorting
    var order = [];
    $.each(this.context[0].aaSorting, function(key, value) 
      order.push(
        'column' : value[0], 
        'dir' : value[1]
      );
    );

    // make value for search
    var search = 
      'value' : this.context[0].oPreviousSearch.sSearch, 
      'regex' : this.context[0].oPreviousSearch.bRegex
    ;

    var items = [];
    var status = $('#status').val();
    $.ajax(
      url: "server_side_url",
      data:  columns: columns, order: order, search: search, status: status, page: 'all' 
      success: function (result) 

        $.each(result.data, function(key, value) 

          var item = [];

          item.push(key+1);
          item.push(value.username);
          item.push(value.email);
          item.push(value.created_at);
          item.push(value.status);

          items.push(item);
        );
      ,
      async: false
    );

    return 
      body: items, 
      // skip actions header
      header: $("#user_table thead tr th").map(function()  
        if(this.innerHTML!='Actions')
          return this.innerHTML; 
      ).get()
    ;
  
);

var user_table = $('#user_table').DataTable(
  dom: 'Bfrtip',
  buttons: [
  'copy', 'csv', 'excel', 'pdf', 'print'
  ],
  "oSearch": "bSmart": false,
  processing: true,
  serverSide: true,
  ajax: 
    url: "server_side_url",
    type: 'GET',
    data: function (d) 
      d.status = ""; // when onload make status as empty to get all users
    
  ,
  columns: [
  data: 'DT_RowIndex', name: 'DT_RowIndex',
  data: 'username', name: 'username',
  data: 'email', name: 'email',
  data: 'created_at', name: 'created_at',
  data: 'status', name: 'status',
  data: 'actions', name: 'actions', orderable: false, searchable: false,
  ],
);

// filter users with status
$('#status').change(function() 
  user_table.draw();
);

【讨论】:

您能解释一下这是如何工作的吗? How to answer "简洁是可以接受的,但更全面的解释更好。" How do I write a good answer?。 Stack Overflow 重视完整和完整的答案。您可以编辑您的答案 here 或单击答案左下角的“编辑”按钮。谢谢。【参考方案8】:

如果我们可以事先确定“All”的值,Selcuk 的答案绝对可以正常工作。 假设行数存储在变量 row_count 中。那么

var row_count = $("#row_count").val();
var table = $('#example').DataTable(
    serverSide: true,
    ajax: "/your_ajax_url/",
    lengthMenu: [[25, 100, row_count], [25, 100, "All"]],
    pageLength: 25,
    buttons: [
        
            extend: 'excel',
            text: '<span class="fa fa-file-excel-o"></span> Excel Export',
            exportOptions: 
                modifier: 
                    search: 'applied',
                    order: 'applied'
                
            
        
    ],
    // other options
); 

【讨论】:

【参考方案9】:

我正在使用 Datatables 版本:1.10.15,并且得到了 @kevenpo 的答案。我不得不对其进行一些修改以处理我们的服务器端参数,但这是唯一的绊脚石。我将他的行:data.length = 2147483647; 更改为 data.params[2]= -1;,因为我们将服务器端参数存储在 params 子数组中。我尚未使用非常大的数据集对其进行测试以查看性能如何,但这是一个非常聪明的解决方案。

【讨论】:

请在***编辑器的帮助下正确格式化您的答案【参考方案10】:

只是想为那些苦苦挣扎的人发布一个实际的答案。

如果您使用 excel 按钮导出,您可以使用 customizeData 按钮属性在导出前将数据格式化为 excel。

我使用它对我的服务器进行同步 api 调用以获取数据、返回数据、按摩数据,然后让它继续运行。代码如下。

                           
                extend: 'excel',
                customizeData: function (p)
                
                    //get the params for the last datatables ajax call
                    var params = JSON.parse(options.dataTable.ajax.params());
                    //flag to tell the server to ignore paging info and get everything that matches the filter
                    params.export = true;
                    UC.Api(options.api.read.getHook(), params, function (data)
                    
                        p.body = new Array();
                        $.each(data.data, function (i, d)
                        
                            var item = [d.serial, UC.FormatDateToLocal(d.meta.Date), d.transmission.title, d.transmission.type, d.transmission.information];
                            p.body.push(item);
                        );
                    , null,  async: false );
                
            ,

【讨论】:

【参考方案11】:

@diogenesgg 的回答很好!

但我检查了$.fn.DataTable.Api.register 不支持Promise

所以,我先获取数据。

    const data = await selectDailyConnectStatistics(
      page: 1,
      limit: 99999999
    ))
    excelDatas = data.list

    $("#table").DataTable().button('.buttons-excel').trigger();

二次触发excel导出。

  let excelDatas = []
  $.fn.DataTable.Api.register('buttons.exportData()', function(options) 
    if (this.context.length ) 
      return 
        body: _.map(excelDatas, v=> [v.data, ...]), 
        header: ['colum header name', ...]
      
    
  );

【讨论】:

【参考方案12】:

您可以在页面中制作一个隐藏的额外表格,然后制作一个按钮以下载所有数据,分配此代码以使用这些选项将隐藏表格作为所有行的数据表

var options = 
            "processing": true,
            "serverSide": true,
            "ajax": fullbase,
            "language": 
                "search": "Buscar: ",
                "zeroRecords": "Datos no encontrados."
            ,
            "initComplete": function(settings, json) 
                $(".buttons-excel").click();
            ,
            "iDisplayLength": 100000,
            lengthMenu: [[10,25,50,100, 100000], [10,25,50, 100, "All"]],
            "buttons": [
                extend : 'excel',
                exportOptions : 
                        order : 'current',  
                        page : 'all',    
                        search : 'none' 
                
            ],
            "dom": "Blfrtip",
        ;

您可以在完整表格事件中看到导出 excel 按钮的触发器,并将自动为用户运行 当用户点击那个按钮然后得到一个包含所有数据的excel

【讨论】:

以上是关于如何使用 Ajax 从数据表中导出所有行?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用beeline cli从hive表中导出数据

如何从时间分析器仪器中导出 cpu 使用情况

如何从phpmyadmin中的数据库中的特定表中导出特定列

如何从密钥库中导出 .key 和 .crt

使用哪个工具从 ODBC 数据库中导出 SQL 模式?

如何从sklearn LinearRegression中导出线性回归公式