codeinprogress codeinprogress - 2 months ago 15
Node.js Question

How to give download window option to user in a node.js express application

I am building a node.js + express application. An excel file gets created on my server side and I need to serve it to the user in the download window. So the user can choose his desired directory and then download it. After the user does that, I want to then delete that file (which gets created in the app's local download folder).

Here is my code:

router.post('/filter', function(req, res){
try {
var root = path.dirname(require.main.filename);
var downloadsDir = root + '/downloads';
var workbook = new Excel.Workbook();

var worksheet = workbook.addWorksheet('My Sheet');
worksheet.columns = [
{ header: 'Id', key: 'id', width: 10 },
{ header: 'Name', key: 'name', width: 32 },
{ header: 'D.O.B.', key: 'DOB', width: 10 }
];
worksheet.addRow({id: 1, name: 'John Doe', dob: new Date(1970,1,1)});
worksheet.addRow({id: 2, name: 'Jane Doe', dob: new Date(1965,1,7)});

//ensure a download directory exist, if not then create it
fs.ensureDir(downloadsDir, function (err) {
console.log(err);
workbook.xlsx.writeFile(downloadsDir + '/temp.xlsx').then(function() {
console.log('file is written');
var file = downloadsDir + '/temp.xlsx';

// fs.emptyDir(downloadsDir + '/', function (err) {
// if (err) return console.error(err)
// console.log('success!')
// });
res.send(file);
});
});
} catch(err) {
console.log('OOOOOOO this is the error: ' + err);
}
});


This code is called from an ajax function on my public javascript

$('.filter-accounts-btn').click(function(){
$.ajax({
type: 'post',
url: '/accounts/filter',
success: function(data) {
console.log('------- data: ' + data);
window.open(data, '_blank');
},
error: function(err) {
console.log('------------ Error: ' + err);
}
});
});


However it does not seem to work. Obviously there is something wrong with it. When I run my app on Heroku, the new page that opens has the url :


https://myapp.com/app/downloads/temp.xlsx


It shows me error. I also tried using an npm module called open where I would just open the excel for the user. But it caused the app to crash.

open(file, function (err) {
if ( err ) throw err;
});


When tested locally, it opens the excel.

Is there a better way to do this? Also, how would I know if the user has downloaded the file? Right now it is saved in the downloads folder, but I would like to empty that folder (using the commented code) as soon as the file is downloaded by the user.

************* Edit*********************

On suggestion, I have tried this new code

workbook.xlsx.writeFile(tempfile('.xlsx')).then(function() {
console.log('file is written');
res.download(tempfile('.xlsx'), function(err){
console.log('---------- error downloading file: ' + err);
});
});


The res.downlaod throws an error

enter image description here


Error: ENOENT, stat
'/var/folders/nk/2w94bk3j5fs976d_dj5vwsvr0000gn/T/54f77d41-db1e-4dcf-8aea-7f88d682c436.xlsx'


Is it not able to find the temp file?

Answer

The first advice is to stop storing the file with your code if it's not meant to last long. A better approach will be to use the temporal directory of the machine and letting the OS clean for you. You can use tempfile in order to generate a full path to a temporal file.

The second advice is to avoid splitting the file generation and download steps. You can use res.download() api on the server to instruct the browser into presenting the download windows directly to the user. That is: instead of sending to the browser the file name to download on the next step, you send directly the file content to download and a special header to instruct the client browser you want the client to download the file.

The latter require you to modify the Jquery(?, correct me if i'm wrong here) code on the browser. Once the post request is made and the server response is received the browser should present the download dialog directly.

Try this:

var tmp = tempfile('.xlsx');
workbook.xlsx.writeFile(tmp).then(function() {
    console.log('file is written');
    res.download(tmp, function(err){
        console.log('---------- error downloading file: ' + err);
    });
});