1

I'm trying to get access to a pdf from a Google Drive. Whether this access is downloading or viewing, it doesn't really matter, it just needs to be available.

I am using Javascript and NodeJS, with express and google drive api.

I have the following function below which downloads a pdf. But silly me thought this was correct because it worked locally. Then when I deployed it I realised the target filepath no longer makes sense.

function downloadDoc (sourceID, targetName, callback) {
  const dest = fs.createWriteStream(`${os.homedir()}/downloads/`+targetName);
  drive.files.get(
    {sourceID, alt: 'media'},
    {responseType: 'stream'},
    (err, res) => {
      if (err) {
        console.error(err);
      }
      res.data.on('end', () => {
        console.log('Done downloading file.');
        callback();
      })
       .on('error', err => {
          console.error('Error downloading file.');
          throw err;
        })
        .pipe(dest);
    });
}

So what I need to do, is take the data (or response?) from this function and send it over to client side. I assume this is simple to do but, being a simple man, I find myself stuck. I have written this, with the intention that a user can click a link on client side, requesting this URL and calling the function to download the pdf.

app.get('/download_pdf', (req, res) => {
    downloadDoc(documentID, 'docName.pdf', ()=>{ 
      console.log("downloaded pdf");
    });
    res.end();
});

I'm thinking I need to change the argument provided to pipe() since obviously I can't use the filepath.

Similar questions I've checked:
Display Pdf in browser using express js

How to send a pdf file from Node/Express app to the browser

Send pdf via express to js client and download

While these questions are very related to this, I think my issue is due to a lack of understanding of callbacks or requests/responses. I want to learn this properly - not just ask for answers - but as it is, I'm becoming very pressed for time and need a solution soon.

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
Updog
  • 321
  • 1
  • 2
  • 8

2 Answers2

2

You should be able to simply pipe the returned readable stream to the express res object (which is a writeable stream):

app.get('/download_pdf', (req, res) => {
    drive.files.get({
        fileId: "your-file-id-here",
        alt: 'media'
    })
    .on('end', function () {
        console.log('Done');
    })
    .on('error', function (err) {
        console.log('Error during download', err);
    })
    .pipe(res);    
});

Edit:

as mentioned here, drive.files.get does return a promise. So you need to change it to:

app.get('/download_pdf', (req, res) => {
    drive.files.get({
        fileId,
        alt: 'media'
    }, {
        responseType: 'stream'
    }).then(response => {
        response.data
        .on('end', function () {
            console.log('Done');
        })
        .on('error', function (err) {
            console.log('Error during download', err);
        })
        .pipe(res);
    });
});
eol
  • 23,236
  • 5
  • 46
  • 64
  • Hi, thanks for the reply. I tried this and get problems with the documentID. If I provide the ID directly as a string then I get a syntax error: Unexpected token ',' If I pass it a variable containing the ID then it runs but says 'missing required parameters: fileId' – Updog Nov 02 '20 at 14:08
  • e.g. drive.files.get({'xfg3H32randomStringKdCt4X', alt: 'media'}, {responseType: etc etc....} – Updog Nov 02 '20 at 14:09
  • Just pass it the way you did before through `sourceID`, I edited the answer. – eol Nov 02 '20 at 14:38
  • I still get this error... (node:10008) UnhandledPromiseRejectionWarning: Error: Missing required parameters: fileId If I change the name of it to fileId, for some reason it passes that error, but gives a new one saying that driveStream.pipe is not a function – Updog Nov 02 '20 at 14:44
  • Then you should have seen this error before as well :) Try replacing `sourceID` with `fileId`. See https://developers.google.com/drive/api/v3/reference/files/get#javascript – eol Nov 02 '20 at 14:47
  • 1
    thanks, I have replaced it with fileId but now I get: 'TypeError: driveStream.pipe is not a function' Thanks for your help, I will continue to look into this – Updog Nov 02 '20 at 14:56
  • Maybe check this also: https://github.com/googleapis/google-api-nodejs-client/issues/1971 – eol Nov 02 '20 at 15:16
  • Unfortunately, I cannot get the example from that link to work either - it just times out. It also looks like they are saving the pdf on the server end. I need to pass it to the client side. – Updog Nov 02 '20 at 16:40
  • I've update my code once more, can you give it another shot? Seems like you need to pipe it directly without awaiting it first. – eol Nov 02 '20 at 16:44
  • Sorry but no, I am still receiving an error. Now, it is: UnhandledPromiseRejectionWarning: TypeError: drive.files.get(...).on is not a function. I'm leaving the office now, but I appreciate your help. I will have another go tomorrow. – Updog Nov 02 '20 at 17:05
  • Weird, sorry I couldn't be of more help. Hopefully you figure it out tomorrow :) – eol Nov 02 '20 at 17:10
  • 1
    So I solved it by making a very slight change to the code you provided. I tried to edit your answer to add this but the edit queue is full apparently. If you change it at some point I'll mark your answer as the solution. Thanks again! – Updog Nov 03 '20 at 15:21
  • Happy to hear that - totally missed that, good catch :) I've edited the answer. – eol Nov 03 '20 at 16:26
0

So I figured out a way. I'm not sure if this is bad practice but it seems to work. I distinguished between the two response objects by referring to one as response and one as res.

app.get('/download_pdf', (req, res) => {
    const docId = req.query.id;
    drive.files.get({
        fileId: docId,
        alt: 'media'
    }, {
        responseType: 'stream'
    }).then(response => {
        response.data
        .on('end', function () {
            console.log('Done');
        })
        .on('error', function (err) {
            console.log('Error during download', err);
        })
        .pipe(res);
    });

});

Posting this in the event that somebody else has a similar issue.

Updog
  • 321
  • 1
  • 2
  • 8