I want to downaload a large zip file of 3GB consisting 2000 or more images with their thumbnails to mobile application depending on user’s request from my NodeJS Express server using a route.
Zip file is created using archiver inside a worker. it works fine up to this point.
On my local machine it is too-good but on production, sometimes it works really fast when streaming and sometimes it takes too-long that it reaches timeout after 5 to 10% of download only. Even at that time there no such load on server.
Following is something like my implementation. I can’t figure out the actual problem.
Is it beacuse when my internet is slow, the server sends data very slowly to match my download speed or it’s something else??
const fs = require('fs');
const fsPromises = require('fs/promises');
const crypto = require('crypto');
const path = require('path');
const { logger } = require('config/winston');
const downloadZip = async (req, res) => {
// Create archive
const fnm = 'file_' + crypto.randomBytes(8).toString('hex');
const zipName = fnm + 'zip';
const zipFilePath = path.join('tmpfolder', zipName);
// Get archive from worker
try {
const images = await getImages();
zipStatus = await zipWorker(images);
} catch (err) {
throw `Error in creating zip.`;
}
// Get stats
let zipSize = 0;
try {
const zipStats = await fsPromises.stat(zipFilePath);
zipSize = zipStats.size || 0;
} catch (err) {
throw `Zip stats not found.`;
}
// Download
logger.verbose(`Preparing to download.`);
res.setHeader('Content-Type', 'application/zip');
res.setHeader('Content-Disposition', `attachment; filename=images.zip`);
res.setHeader('Content-Length', zipSize);
const resFile = fs.createReadStream(zipFilePath);
resFile.pipe(res);
resFile.on('error', err => {
logger.error(`Error in streaming zip.`);
res.end();
});
// Print downloading percent on server
let bytesSent = 0;
resFile.on('data', chunk => {
if (zipSize > 0) {
bytesSent += chunk?.length;
const percentage = ((bytesSent / zipSize) * 100).toFixed(2);
logger.verbose(`Downloading ${percentage}%`); // <== Downloading completed in %
}
});
// If completed
resFile.on('end', () => {
logger.verbose(`Images downloaded.`);
});
res.on('close', () => {
resFile.destroy(); // Close the file stream immediately
});
res.on('finish', () => {
logger.verbose(`File streamed successfully.`);
});
};