I have to download a json file using nodejs and react. So, when the user clicks on download button, a request is sent to nodejs, then I make an http request passing the user, pass, and the url to download json file on the server and then download this file on client side
The file download on server side takes almost 5 min, the problem is that I'm getting an network error before the download file is finished:
GET http://127.0.0.1:4000/downloadFile net::ERR_EMPTY_RESPONSE
Error: Network Error
at createError (createError.js:17)
at XMLHttpRequest.handleError (xhr.js:80)
When download button is clicked this function on react side is called:
downloadFile() {
this.setState({ buttonDownloadText: 'Wait ...' })
axios.get(process.env.REACT_APP_HOST + '/downloadFile')
.then(resp => {
window.open(process.env.REACT_APP_HOST + '/download?namefile=' + resp.data.fileName, '_self')
this.setState({ buttonDownloadText: 'Start download' })
})
.catch((error) => {
console.log(error) //here is where the Network Error is returned
this.setState({ buttonDownloadText: 'Error. Try again' })
})
}
Server side:
const http = require('http');
const fs = require('fs');
app.get('/downloadFile', function (req, res, next) {
try {
const headers = new Headers();
const URL = env.URL_API + '/api/downloadFile'
const DOWNLOADURL = env.URL_API + '/api/download'
headers.set('Authorization', 'Basic ' + base64.encode(username + ":" + password));
fetch(URL, {
method: 'GET',
headers: headers,
})
.then(r => r.json())
.then(data =>
fetch(DOWNLOADURL + '/' + data.fileName, {
method: 'GET',
headers: headers,
}).then(result => {
const url = 'http://user:pass@url' + data.fileName
const arq = __dirname + '/download/' + data.fileName
var file = fs.createWriteStream(arq);
http.get(url, function (response) {
response.pipe(file);
file.on('finish', () => file.close(() => res.status(200).send({ file: data.fileName });)) //I have to return the file name to react function
});
}).catch(error => {
logger.error(`${error}`);
})
})
.catch(error => {
logger.error(`${error.status || 500} - ${error} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
res.sendStatus(500)
})
....
});
downloadFile
function must return the fileName, so I call download route to make a download on clint side
app.get('/download', function (req, res) {
try {
const file = __dirname + '/download/' + req.query.namefile;
res.download(file);
} catch (error) {
logger.error(`${error.status || 500} - ${error} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
}
});
So, my point is, Why my downloadFile
route is not waiting the download file finish to return it res.status(200).send({ file: data.fileName })?
As browsers cannot be forced to wait for a long response from the server, I suggest a different approach: upload the file to the cloud (e.g. AWS or Firebase) and return a link to the client.
If using Firebase (or another real time database) the server can set a flag in the db as the file upload is finished. Therefore client gets notified asynchronously when the file is available for the download.