-
-
Save senthilmpro/072f5e69bdef4baffc8442c7e696f4eb to your computer and use it in GitHub Desktop.
'use strict' | |
const Fs = require('fs') | |
const Path = require('path') | |
const Axios = require('axios') | |
async function downloadImage () { | |
const url = 'https://unsplash.com/photos/AaEQmoufHLk/download?force=true' | |
const path = Path.resolve(__dirname, 'images', 'code1.jpg') | |
// axios image download with response type "stream" | |
const response = await Axios({ | |
method: 'GET', | |
url: url, | |
responseType: 'stream' | |
}) | |
// pipe the result stream into a file on disc | |
response.data.pipe(Fs.createWriteStream(path)) | |
// return a promise and resolve when download finishes | |
return new Promise((resolve, reject) => { | |
response.data.on('end', () => { | |
resolve() | |
}) | |
response.data.on('error', () => { | |
reject() | |
}) | |
}) | |
} | |
async function Main(){ | |
const data = await downloadImage(); | |
console.log("DATA ", data); | |
} | |
Main(); |
Isn't it better to call resolve() at the end of writing to disk?
const writer = Fs.createWriteStream(path)
...
response.data.pipe(writer)
...
return new Promise((resolve, reject) => {
writer.on('finish', resolve)
writer.on('error', reject)
})
Isn't it better to call resolve() at the end of writing to disk?
const writer = Fs.createWriteStream(path) ... response.data.pipe(writer) ... return new Promise((resolve, reject) => { writer.on('finish', resolve) writer.on('error', reject) })
I agree with you. In the shown example the promise is resolved when the file finishes download.
But in your example the promise is resolved when BOTH
-
the file finished download AND
-
the file is successfully written to the new path on disk
If u don't want to use streams you can set responseType: "arraybuffer"
like this
const res = await axios.get(url, { responseType: "arraybuffer" });
await fs.promises.writeFile(downloadDestination, res.data);
Thx friend. This snippet rly helped me :)
Is it possible to do this all in memory instead of writing a file?
Is it possible to do this all in memory instead of writing a file?
instead of doing a response.pipe into a file..
use a library like https://github.com/paulja/memory-streams-js and write your streams into memory.
Isn't it better to call resolve() at the end of writing to disk?
const writer = Fs.createWriteStream(path) ... response.data.pipe(writer) ... return new Promise((resolve, reject) => { writer.on('finish', resolve) writer.on('error', reject) })
This was great! I've modified this a little. Here is my module:
const fs = require('fs')
const axios = require('axios')
const _ = require('underscore')
module.exports = {
downloadFile: async function (src, dest) {
// axios download with response type "stream"
return await axios({
method: 'GET',
url: src,
responseType: 'stream'
})
.then(function (response) {
if (response && response.status === 200 && !_(response.data).isUndefined() && !_(response.data).isNull()) {
let writer = fs.createWriteStream(dest)
// pipe the result stream into a file on disc
response.data.pipe(writer)
// return a promise and resolve when download finishes
return new Promise((resolve, reject) => {
writer.on('finish', () => {
resolve(true)
})
writer.on('error', (error) => {
reject(error)
})
})
}
})
.catch(function (e) {
throw new Error(e)
});
}
}
Usage:
const downloader = require('../utils/download')
...
downloader.downloadFile(src, dest)
.then(function (result) {
if (result === true) {
...
}
})
.catch(function (e) {
console.log(e)
});
Dzięki wielkie mam nadziej ze za działa
This got me going - and annoyed - by nodejs.
I wanted to try the fs
promises version of fs to handle a download. Turns out that it's quite different and even feels c-like.
Here's a download function that uses fs promises and axios as http handler:
const axios = require('axios');
const fs = require('fs/promises');
const downloadFile = async (url, destination) => {
let file = null;
try {
console.log(`Starting file download with url ${url}`)
const response = await axios({
method: 'GET',
url: url,
responseType: 'stream'
})
file = await fs.open(destination, 'w')
const writer = file.createWriteStream()
response.data.pipe(writer)
await new Promise((resolve, reject) => {
writer.on('finish', async () => {
console.log(`Completed download with url ${url}`)
resolve()
})
writer.on('error', reject)
})
} catch (error) {
throw new Error(error);
} finally {
file?.close()
}
}
Frankly, it's ugly. I thought promises were to make things easier and prettier (and they often are!), but the case of downloading a file (specially the writer object that you need to promisify still) just makes it look messy.
Note the use of finally
to close the file handle. The ?
part in file?.close()
handles the error case neatly.
Also note that this means you must not return within the try block else the finally
won't be called.
Call with something like:
await downloadFile("https://whatever/url/to/file", "/destination/file"
);
@markg85 This is incorrect:
Also note that this means you must not return within the try block else the finally won't be called.
The finally
block always gets called. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#finallystatements for reference.
Maybe you were not "awaiting" inside the try and when the finally
got executed the state of your code was unexpected?
Also, no need to create a Promise at the end of the try. And, since the catch is not doing anything else than jus rethrowing the error, that can be removed too.
The simplified version:
const axios = require("axios");
const fs = require("fs");
const downloadFile = async (url, destination) => {
const writer = fs.createWriteStream(destination, { flags: "w" });
console.log(`Starting file download with url ${url}`);
const response = await axios({
method: "GET",
url: url,
responseType: "stream",
});
const res = await response.data.pipe(writer);
console.log(`Completed download with url ${url}`);
return res;
};
@leite08 Thank you for correcting me! This was a while ago so i don't know the reason for saying what i did there. I must have had a reason... Don't know what though.
A couple fun notes though :)
- You are assuming default autoClose = true behavior on the write object. This default has changed over time (click history for that function) so i'd prefer to go with an explicit route in this case.
- You always open a file, even if there would be an error on axios. I'm not sure what happens to that file in this case, i'm guessing just an empty file. I'd put the
writer
object below the response. That way you'd at least know the file is opened on a successful http response. It can still fail but that's much less of an edge case then you have now. - You're cheating 😄 The createWriteStream you use is the callback API, i explicitly tried to use the promises API (this function)
Care to go for a second try with the promises api instead? I'm just really curious what you come up with!
Sure. Updated the code.