I am working on uploading and downloading file (docx, pdf, text, etc) through nodejs request-promise
and request
libraries.
Problem with request-promise
is that they don't promisify pipe
method from request
package. Hence, we need to do it in the old way.
I was able to come up with the hybrid solution, where I was able to use async/await
and Promise()
at same time. Here is the example:
/**
* Downloads the file.
* @param {string} fileId : File id to be downloaded.
* @param {string} downloadFileName : File name to be downloaded.
* @param {string} downloadLocation : File location where it will be downloaded.
* @param {number} version : [Optional] version of the file to be downloaded.
* @returns {string}: Downloaded file's absolute path.
*/
const getFile = async (fileId, downloadFileName, downloadLocation, version = undefined) => {
try {
const url = version ? `http://localhost:3000/files/${fileId}?version=${version}` :
`${config.dms.url}/files/${fileUuid}`;
const fileOutputPath = path.join(downloadLocation, fileName);
const options = {
method: 'GET',
url: url,
headers: {
'content-type': 'application/json',
},
resolveWithFullResponse: true
}
// Download the file and return the full downloaded file path.
const downloadedFilePath = writeTheFileIntoDirectory(options, fileOutputPath);
return downloadedFilePath;
} catch (error) {
console.log(error);
}
};
As you can see in above getFile
method, we are using latest ES supported async/await
functionality for asynchronous programming. Now, lets look into writeTheFileIntoDirectory
method.
/**
* Makes REST API request and writes the file to the location provided.
* @param {object} options : Request option to make REST API request.
* @param {string} fileOutputPath : Downloaded file's absolute path.
*/
const writeTheFileIntoDirectory = (options, fileOutputPath) => {
return new Promise((resolve, reject) => {
// Get file downloaded.
const stream = fs.createWriteStream(fileOutputPath);
return request
.get(options.url, options, (err, res, body) => {
if (res.statusCode < 200 || res.statusCode >= 400) {
const bodyObj = JSON.parse(body);
const error = bodyObj.error;
error.statusCode = res.statusCode;
return reject(error);
}
})
.on('error', error => reject(error))
.pipe(stream)
.on('close', () => resolve(fileOutputPath));
});
}
The beauty of nodejs is that it support backward compatibility of different asynchronous implementation. If a method is returning promise, then await
will be kicked in and will wait for the method to be completed.
Above writeTheFileIntoDirectory
method will download the file and will return positively when the stream is closed successfully, else it will return error.