I am using new feature in Datatables: \"HTML5 export buttons\". I am loading data with Ajax.
https://datatables.net/extensions/buttons/examples/html5/simple.html
I know this is an old question, however for anyone struggling with this, here's my solution.
Variables:
var downloading = false,
downloadTimestamp = null;
Download button definition:
buttons: [{
text: '',
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 definition:
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' function:
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
}
}
Server-side:
if(download == false), then server executes SELECT columns FROM tables WHERE rowNumber BETWEEN firstRow AND lastRow and outputs result for normal display within DataTable.
if(download == true), then server executes SELECT columns FROM tables and stores all rows formatted as CSV file (or any other file format depending on what Your server environment is capable to produce) server-side for later retrieval by GET request.
Following is ASP JScript code that I've used server-side:
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 = []