Writing the stream returned by node-fetch
Asked Answered
C

2

15

the README contains the following code as an example of writing a file fetched:

fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')
    .then(res => {
        const dest = fs.createWriteStream('./octocat.png');
        res.body.pipe(dest);
    });

to which I would concatenate another .then() to use the file written to disk. in reality it appears that my code runs before the file has finished writing to disk, so it occurs to me that the promise returned by the above code should take into consideration the issuance of the 'end' event on the stream. maybe something like this:

fetch(url).then(res => new Promise((resolve, reject) => {
    const dest = fs.createWriteStream(fn);
    res.body.pipe(dest);
    res.body.on('end', () => resolve());
    dest.on('error', reject);
}));

but when I try to run this code it complains:

TypeError: res.body.on is not a function

which makes sense given that res.body looks like this:

{ pipe: [Function: pipe] }

which is to say, is not a stream at all

two questions: 1) how can I get access to the actual stream? 2) if I have no access to it, how can I know when the read stream has closed so I can resolve?

p.s.

and no, I can't get the stream from .pipe(), which returns undefined

Concubine answered 26/3, 2019 at 4:9 Comment(0)
S
23

As far as I can tell, your code is correct. I ran

const fs = require("fs");
const fetch = require("node-fetch");

fetch("https://assets-cdn.github.com/images/modules/logos_page/Octocat.png")
  .then(
    res =>
      new Promise((resolve, reject) => {
        const dest = fs.createWriteStream("./tmp.txt");
        res.body.pipe(dest);
        res.body.on("end", () => resolve("it worked"));
        dest.on("error", reject);
      })
  )
  .then(x => console.log(x));

and it worked exactly as expected and printed "it worked"

Serf answered 26/3, 2019 at 4:32 Comment(7)
maybe it has to do with the version of node.js... I'm running 11.5.0 and it breaks as I indicatedConcubine
@Concubine Hmm I'm on v11.12.0. It breaks even if you copy-paste the above and run it?Serf
I tested it. it worked. I'm a bit confused how that could be. looking into it... thanksConcubine
I found my problem. it was a bad mock in the tests. thank you for following up on thisConcubine
there is async version ?Rosannrosanna
Great answer, thank you ; Do you know how to get the content-type response header of the res.body?Doane
Hi, I am trying to return the binary data once node finishes reading it. Can you tell me how to do it?Nob
C
11

I figured it out. the writeable stream will automatically close when the readable does. so I can hook onto that instead:

fetch(url).then(res => new Promise((resolve, reject) => {
    const dest = fs.createWriteStream(fn);
    res.body.pipe(dest);
    dest.on('close', () => resolve());
    dest.on('error', reject);
}));
Concubine answered 26/3, 2019 at 5:21 Comment(1)
just wanted to add fn is filename in the above exampleSteeplebush

© 2022 - 2024 — McMap. All rights reserved.