如何使用 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 从数据表中导出所有行?的主要内容,如果未能解决你的问题,请参考以下文章