Unable to download file via Google Drive API - javascript

Node.js v10.15.2, googleapis v40.0.0
I tried this
const dest = fs.createWriteStream(`/Users/user/Downloads/${fileId}.mp4`);
drive.files.get({
fileId,
alt: 'media'
},
{
responseType: 'stream'
}, (err, res) => {
res.data.on('end', () => {
console.log('Done downloading file!');
})
.on('error', console.error)
.pipe(dest);
});
And got error
$ node .
Files:
20140810_125633.mp4 (1SwYm5Z1zPczZnDulmsbA9wrEJ-JT-hwE)
Getting started (0B3K2QXOGSOFRc3RhcnRlcl9maWxl)
(node:62335) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'data' of undefined
at drive.files.get (/Users/user/Development/app/google_drive_integration/index.js:104:9)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:62335) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:62335) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:62335) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'data' of undefined
at drive.files.get (/Users/user/Development/app/google_drive_integration/index.js:104:9)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:62335) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
Also, I tried this example from Google API reference.
const dest = fs.createWriteStream(`/Users/user/Downloads/${fileId}.mp4`);
drive.files.get({
fileId,
alt: 'media'
})
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(dest);
And got error
$ node .
Files:
20140810_125633.mp4 (1SwYm5Z1zPczZnDulmsbA9wrEJ-JT-hwE)
(node:62336) UnhandledPromiseRejectionWarning: TypeError: drive.files.get(...).on is not a function
at downloadFile1 (/Users/user/Development/app/google_drive_integration/index.js:118:8)
at files.map (/Users/user/Development/app/google_drive_integration/index.js:87:9)
at Array.map (<anonymous>)
at drive.files.list (/Users/user/Development/app/google_drive_integration/index.js:84:13)
at createAPIRequestAsync.then.r (/Users/user/Development/app/google_drive_integration/node_modules/googleapis-common/build/src/apirequest.js:48:53)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:62336) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:62336) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:62336) UnhandledPromiseRejectionWarning: Error: The user has not granted the app 619229308650 read access to the file 1SwYm5Z1zPczZnDulmsbA9wrEJ-JT-hwE.
at Gaxios.<anonymous> (/Users/user/Development/app/google_drive_integration/node_modules/gaxios/build/src/gaxios.js:73:27)
at Generator.next (<anonymous>)
at fulfilled (/Users/user/Development/app/google_drive_integration/node_modules/gaxios/build/src/gaxios.js:16:58)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:62336) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
I list file 20140810_125633.mp4 successfully but can't download it. What am I doing wrong?
All the code I have
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';
// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Drive API.
authorize(JSON.parse(content), listFiles);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getAccessToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* #param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback for the authorized client.
*/
function getAccessToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
/**
* Lists the names and IDs of up to 10 files.
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listFiles(auth) {
const drive = google.drive({version: 'v3', auth});
drive.files.list({
q: "mimeType != 'application/vnd.google-apps.folder'",
pageSize: 1000,
fields: 'nextPageToken, files(id, name)',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const files = res.data.files;
if (files.length) {
console.log('Files:');
files.map((file) => {
console.log(`${file.name} (${file.id})`);
downloadFile0(drive, file.id);
downloadFile1(drive, file.id);
});
} else {
console.log('No files found.');
}
});
}
function downloadFile0(drive, fileId) {
const dest = fs.createWriteStream(`/Users/user/Downloads/${fileId}.mp4`);
drive.files.get({
fileId,
alt: 'media'
},
{
responseType: 'stream'
}, (err, res) => {
res.data.on('end', () => {
console.log('Done downloading file!');
})
.on('error', console.error)
.pipe(dest);
});
}
function downloadFile1(drive, fileId) {
const dest = fs.createWriteStream(`/Users/user/Downloads/${fileId}.mp4`);
drive.files.get({
fileId,
alt: 'media'
})
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(dest);
}

In API v3 you get a promise from "drive.files.get" and on the promise resolve you have the "data" stream
function downloadFile(drive, fileId, fileName) {
const filePath = `/Users/user/Downloads/${fileName}`;
const dest = fs.createWriteStream(filePath);
let progress = 0;
drive.files.get(
{ fileId, alt: 'media' },
{ responseType: 'stream' }
).then(res => {
res.data
.on('end', () => {
console.log('Done downloading file.');
})
.on('error', err => {
console.error('Error downloading file.');
})
.on('data', d => {
progress += d.length;
if (process.stdout.isTTY) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(`Downloaded ${progress} bytes`);
}
})
.pipe(dest);
});
}

Related

NodeJS - How to deal with UnhandledPromiseRejectionWarning?

I am fairly new to Node JS and so I am struggling a bit.
I am trying to read files for google drive using their API, from my Node Js code.
I am having the following code
router.get('/', function (req, res, next) {
var pageToken = null;
// Using the NPM module 'async'
async.doWhilst(function (callback) {
drive.files.list({
q: "mimeType='image/jpeg'",
fields: 'nextPageToken, files(id, name)',
spaces: 'drive',
pageToken: pageToken
}, function (err, res) {
if (err) {
// Handle error
console.error(err);
callback(err)
} else {
res.files.forEach(function (file) {
console.log('Found file: ', file.name, file.id);
});
pageToken = res.nextPageToken;
callback();
}
});
}, function () {
return !!pageToken;
}, function (err) {
if (err) {
// Handle error
console.error(err);
} else {
// All pages fetched
}
})
res.render('index', { title: 'Express' });
});
The above code is giving me the following error when i send the get request
(node:13884) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'forEach' of undefined
at E:\nodejs\newcelebapi\routes\index.js:49:17
at E:\nodejs\newcelebapi\node_modules\googleapis-common\build\src\apirequest.js:43:53
at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:13884) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:13884) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
The issue is in the following line
res.files.forEach(function (file) {
I've tried everything I could and gave up understanding the problem.
Could you please help me with this?
Thanks!
Per this example in the doc, you want to be using:
res.data.files.forEach(...)
not:
res.files.forEach(...)
And, it looks like as soon as you get by that problem, you will have another problem because you are calling res.render() in the wrong place. And, when you fix that, you will have an issue with redefining res in your Google callback which will hide the higher level res that you need for res.render().
I would strongly recommend that you not use the async library here. It doesn't seem like it's needed here and it just complicates things. And, if you did need help doing coordination of asynchronous operations, promises is the modern way to do so.
You don't show what you're trying to do with the resulting files (other than logging them), but here's a simple implementation that does that:
router.get('/', function (req, res, next) {
var pageToken = null;
drive.files.list({
q: "mimeType='image/jpeg'",
fields: 'nextPageToken, files(id, name)',
spaces: 'drive',
pageToken: pageToken
}).then(response => {
let files = response.data.files;
files.forEach(file => console.log('Found file: ', file.name, file.id))
res.render('index', { title: 'Express' });
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
});
Note, that I named the parameter from drive.files.list() to be named response instead of res so I can access both res from the router.get() callback and response from the drive.files.list() callback. You gave them both the same name res which means you can't access the router.get() parameter of the same name.

Properly handled Promise Rejections

Considering the following code:
db.js
// Connecting to catalogstore (mongodb)
const mydb = async () => {
try {
await mongoose.connect(process.env.db);
console.log("Connected to Database!");
}
catch (err) {
throw new Error("Database connection error:", err);
}
};
export { db }
app.js
import { db } from './db';
db().then(async() => {
try {
let server = app.listen(process.env.port,
process.env.host, function() {
let host = server.address().address;
let port = server.address().port;
console.log('App started');
});
} catch (err) {
console.log(err);
}
});
Basically I'd like to start an Express server only after establishing the db connection.
It actually works fine, however I get this warning:
(node:29892) UnhandledPromiseRejectionWarning: Error: Database connection error:
at catalogstore (/Users/notaris/Workspace/Google/gcp-devops/apps/catalogservice/src/db.js:44:11)
at processTicksAndRejections (internal/process/task_queues.js:89:5)
(node:29892) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:29892) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
How can I properly handle that?
The error is thrown by the db async function.
The correct way to handle this Error (using an async function/arrow) is:
import { db } from './db';
const main = async () => {
try {
await db();
let server = app.listen(process.env.port,
process.env.host, function() {
let host = server.address().address;
let port = server.address().port;
console.log('App started');
});
} catch (err) {
console.log(err);
}
});
main();
Modify app.js like follow
import { db } from './db';
db()
.then(/* normal logic */)
.catch(/* error logic */)
Just catch it ^^
In debugger i see that you should catch the error in the promise like this :
//app.js
import { db } from './db';
db().then(async() => {
try {
let server = app.listen(process.env.port,
process.env.host, function() {
let host = server.address().address;
let port = server.address().port;
console.log('App started');
});
} catch (err) {
console.log(err);
}
}).catch(error=>{
console.log('error'+error);
});
And you can check this link for more details.

nodejs crash only when I send multiple request

node js thunder when I send multiple requests to the server at the same time, but not when I send one by one. And nodejs says.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
(node:4) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 6)
That error in common when you send two responses to the client but in my code only sent one
This is my code
const User = require('../../../modelos/users');
async function getAllValuationsByUserName(req, res, next) {
let isOwner = false;
const userId = req.params.id;
const pagination = {
skip: Number(req.query.skip),
limit: Number(req.query.limit)
}
if (userId === res.locals.userid) {
isOwner = true;
}
try {
const user = await User.findOne(
{ userName: new RegExp('^' + userId + '$', "i") },
{
'userPhoto.valuations': {
$slice: [pagination.skip, pagination.limit]
}
})
const valuations = user.userPhoto.valuations
const total = user.userPhoto.valuations.length
return res.status(200).send({
erro: false,
isOwner,
valuations,
total
})
} catch (err) {
console.log(err)
return res.status(400).send({
error: true,
inf: err
});
}
}
module.exports = getAllValuationsByUserName

UnhandledPromiseRejectionWarning: undefined in Mongoose

When trying to authenticate users with Mongoose I get the following warning in my console:
(node:20114) UnhandledPromiseRejectionWarning: undefined
(node:20114) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:20114) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Trying to trace the stack doesn't result in anything, all I get is undefined. Similar questions exist here on Stackoverflow, but they don't apply to my situation. Any idea what this could cause?
My route controller is calling the findByCredentials function inside the Mongoose model:
Controller
static login(req, res) {
User.findByCredentials(req.body.email, req.body.password)
.then(user => {
return user.generateAuthToken().then(token => {
res.header("x-auth", token).json(user);
});
})
.catch(error => {
res.status(400).json({ message: "Invalid credentials." });
});
}
Model
userSchema.statics.findByCredentials = function(email, password) {
return this.findOne({ email }).then(user => {
if (!user) {
Promise.reject();
}
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err, res) => {
res ? resolve(user) : reject();
});
});
});
};
The error undefined is coming from your Promise.reject() you are not passing any error message to it, so you are literally throwing undefined.
It is not caught in the catch in login, because you are not returning it from your findByCredentials method.
Solution:
userSchema.statics.findByCredentials = function(email, password) {
return this.findOne({ email }).then(user => {
if (!user) {
return Promise.reject('User not available');
}
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err, res) => {
res ? resolve(user) : reject();
});
});
});
};

Where can I get the file name and line number of a node warning?

I get these cryptic lines here:
DEBUG: Mongoose connected (node:5983)
UnhandledPromiseRejectionWarning: Unhandled promise rejection
(rejection id: 1): TypeError: Cannot read property 'then' of undefined
(node:5983) [DEP0018] DeprecationWarning: Unhandled promise rejections
are deprecated. In the future, promise rejections that are not handled
will terminate the Node.js process with a non-zero exit code.
How can I get useful debugging information so I don't have to guess where the exact issue is?
I believe it is somewhere in this file:
const mongoose = require('mongoose');
const helper = require('../config/helper');
const schema = require('./Schemas')
mongoose.connect(helper.getMongoose()).then(
() => {
console.log('DEBUG: Mongoose connected')
mongooseConnected();
},
(err) => {
console.log('DEBUG: Mongoose did not connect')
}
);
function mongooseConnected () {
makeSchema( schema.User,
{ id_google: '1',
type: 'person',
timestamp: Date.now()
});
}
function makeSchema (Schema, dataObj) {
const Class = mongoose.model('Class', Schema);
const Instance = new Class(dataObj);
Instance.save((err, results)=>{
if (err) {
return console.error(err);
}
}).then(() => {
console.log('Saved Successfully')
});
}
In your case you are providing a callback to your save function, this way mongoose will not return a Promise:
Instance.save((err, results)=>{
if (err) {
return console.error(err);
}
console.log('Saved Successfully')
})
If you still want to use Promise then you don't have to pass a callback function:
Instance.save().then(() => {
console.log('Saved Successfully')
}).catch(err => {
return console.error(err);
});
In general an unhandled Promise rejection means that you're missing a catch method to deal with the error. Simply including .then() after returning a promise only deals with the code if it runs successfully, whereas including a .catch block will skip .then and only run .catch, with the error as a callback when an error occurs while executing the code which returns the promise.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
myModel.save()
.then(() => {
console.log('Saved');
})
.catch(err => {
console.log(err);
}

Categories