Google Drive API uploading files - node.js

I'm trying to figure the google drive api (node.js) out for a client. I need to upload files to their drive. I've decided to use a service account and have the auth working. I'm trying to upload a sample file but I can't find it in my drive. My guess is I need to somehow link it to my drive, but I'm not sure. Here is the code I have based off of an example I found online:
async function upload(){
const drive = google.drive({
version: 'v3',
auth: authorize()
});
const res = await drive.files.create({
requestBody: {
name: 'Test',
mimeType: 'text/plain'
},
media: {
mimeType: 'text/plain',
body: 'Hello World'
}
}, function (err, file) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('File Id: ', file.id);
}
});
authorize() returns the jwtclient and works fine.
console.log(res) returns file id undefined.
any help would be much appreciated!

When the response value is retrieved from googleapis for Node.js, please modify your script as follows.
From:
console.log('File Id: ', file.id);
To:
console.log('File Id: ', file.data.id);
Note:
In your script, I think that you can also use the following script.
const res = await drive.files
.create({
requestBody: {
name: "Test",
mimeType: "text/plain",
},
media: {
mimeType: "text/plain",
body: "Hello World",
}
})
.catch(console.log);
if (res) console.log("File Id: ", res.data.id);
Reference:
googleapis for Node.js

Related

Cannot make a request of image with expo-camera takePictureAsync method to node.js backend server

I am struggling from taking pictures with expo camera on react native and sending the cache image to my node.js backend server and then my backend server appends this to a formdata object and sends it to my webservice. I searched a lot about the operations between my frontend and backend but couldn't find the exact true answer.
My express-node backend server getting images with multer.
I have a react native frontend code like below in order to send my image data I got as returned object of takePictureAsync method of expo-camera:
CLIENT SIDE
//react native client side
const takePicture = async () => {
if (cameraRef.current) {
const options = { quality: 0.5, base64: true, skipProcessing: true };
const data = await cameraRef.current.takePictureAsync(options);
const source = data.uri;
if (source) {
await cameraRef.current.pausePreview();
setIsPreview(true);
uploadFile(source);
console.log('picture source', source);
}
}
};
Then I get 404 status error from my backend when I try to send this image data like below with axios to my node.js backend server:
//react native client side
async function uploadFile(photo) {
const formData = new FormData();
formData.append('file', {
uri: photo,
name: 'test',
mimetype: 'image/jpeg',
});
await axios
.post('http://MyLocalIpAdress:3000/photo-upload', formData, {
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
})
.then((res) => {
console.log(res.data);
return res.data;
});
}
SERVER SIDE
My Node.js backend endpoint is as below:
router.post(
'/photo-upload',
multer({ storage: multer.memoryStorage() }).single('file'),
async (req, res) => {
if (req.file) {
try {
// Transfers uploaded image through webservice
const form = new FormData();
form.append('file', req.file.buffer, {
contentType: req.file.mimetype,
filename: req.file.originalname,
});
res.status(200).send({
message: 'Success'
});
} catch (err) {
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
} else {
return res.status(400).send({ message: 'Please upload a file!' });
}
})
I couldn't figure out whether I'm doing things wrong on server side or client side
and the way of doing it.
I faced same issue with sending image data to backend using formData. There are a couple of tricks to solve this:
Solution 1:
const formdata = new FormData();
formdata.append('image[]', {
name: 'test',
type: imageurl?.type,
uri:
Platform.OS !== 'android'
? 'file://' + photo
: photo,
});
const res = await axios.post('http://MyLocalIpAdress:3000/photo-upload', formdata, {
headers: {
Accept: '*/*',
'Content-type': 'multipart/form-data',
},
});
Solution 2: (My personal choice) is to use a library to upload the data. rn-fetch-blob is something that I have used to solve this. If you plan to use this, go through the documentation and implement it.
RNFetchBlob.fetch('POST', 'http://MyLocalIpAdress:3000/photo-upload',
{
Authorization : "Bearer access-token",
'Content-Type' : 'multipart/form-data',
}, [
// element with property `filename` will be transformed into `file` in form data
{ name : 'avatar', filename : 'avatar.png', data: binaryDataInBase64},
// custom content type
{ name : 'avatar-png', filename : 'avatar-png.png', type:'image/png', data: binaryDataInBase64},
// part file from storage
{ name : 'avatar-foo', filename : 'avatar-foo.png', type:'image/foo', data: RNFetchBlob.wrap(path_to_a_file)},
// elements without property `filename` will be sent as plain text
{ name : 'name', data : 'user'},
{ name : 'info', data : JSON.stringify({
mail : 'example#example.com',
tel : '12345678'
})},
]).then((resp) => {
// ...
}).catch((err) => {
// ...
})

nodejs google drive api file download error with readablestream object

I'm trying to implement a function for downloading a file from the google drive using google drive api V3.
Below is my code.
service.files.get({
auth: this.oauth2Client,
fileId: fileId,
alt: 'media'
}, {responseType: "stream"}, function (err, response) {
console.log(response);
response.data
.on('end',() => {
console.log('Done');
})
.on('error', (err) => {
console.log('Error during download', err);
})
.on('data', d=> {
progress += d.length;
if (process.stdout.isTTY) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write('Download ${progress} bytes');
}
})
.pipe(dest);
})
When I run the code, I get this error.
response.data.on is not a function
Not just the on function, but the pipe function doesn't work either.
I already checked that response.data is the object of Readablestream.
Any ideas?
UPDATES
Below is the full code for GoogleDriveSyn class.
The getAllFilesInFolder function works fine.
const path = require('path')
const {google} = require('googleapis');
const fs = require('fs');
const service = google.drive('v3');
class GoogleDriveSyn {
constructor(auth) {
var apiKeys = JSON.parse(fs.readFileSync('config.json'));
this.oauth2Client = new google.auth.OAuth2(
apiKeys['CLIENT_ID'],
apiKeys['SECRET_ID'],
"http://localhost:8080"
);
this.oauth2Client.credentials = auth;
this.directory = localStorage.getItem('directory');
}
getAllFilesInFolder(folderId) {
var query = '"' + folderId + '"' + ' in parents and mimeType!="application/vnd.google-apps.folder"';
service.files.list({
auth: this.oauth2Client,
q: query,
pageSize: 50,
fields: 'nextPageToken, files(id, name, modifiedTime, kind, createdTime, thumbnailLink, mimeType, size, webContentLink)'
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
console.log(response);
return response
})
}
downloadFile(fileId, filePath, fileName) {
var fullPath = path.join(this.directory, filePath, fileName);
var dest = fs.createWriteStream(fullPath);
let progress = 0;
service.files.get({
auth: this.oauth2Client,
fileId: fileId,
alt: 'media'
}, {responseType: "stream"}, function (err, response) {
console.log(response);
response.data
.on('end',() => {
console.log('Done');
})
.on('error', (err) => {
console.log('Error during download', err);
})
.on('data', d=> {
progress += d.length;
if (process.stdout.isTTY) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write('Download ${progress} bytes');
}
})
.pipe(dest);
})
}
}
module.exports = GoogleDriveSyn;
Also, below is the log of response inside downloadFile function.
{config: {…}, data: ReadableStream, headers: {…}, status: 200, statusText: ""}
config: {url: "https://www.googleapis.com/drive/v3/files/1zcxy0wWsPuWINMY8FP_bjR4nrnj6t8eD?alt=media", method: "GET", responseType: "stream", headers: {…}, paramsSerializer: ƒ, …}
data: ReadableStream {locked: false}
headers: {alt-svc: "h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,…3"; ma=2592000,quic=":443"; ma=2592000; v="46,43"", cache-control: "private, max-age=0, must-revalidate", content-disposition: "attachment", content-length: "12", content-type: "text/plain", …}
status: 200
statusText: ""
__proto__: Object
I believe your goal and situation as follows.
You want to download a text file from the Google Drive.
Your script for authorizing works fine. So this.oauth2Client can be used for downloading the file.
Modification points:
In my environment, I confirmed that your script worked. But in your question, you say the error of response.data.on is not a function occurs. So in this modification, downloadFile was modified as follows.
At first, please confirm the version of googleapis for Node.js you are using. In this case, please use the latest version of googleapis for Node.js. In the current stage, the latest version is googleapis#55.0.0.
If you use the old one, I thought that the reason of your issue might be this.
In your script, 'Download ${progress} bytes' is not correct. In this case, please use it as the template literal. By this, the progress information can be seen.
Modified script:
downloadFile(fileId, filePath, fileName) {
var fullPath = path.join(this.directory, filePath, fileName);
var dest = fs.createWriteStream(fullPath);
let progress = 0;
return drive.files
.get(
{ auth: this.oauth2Client, fileId: fileId, alt: "media" },
{ responseType: "stream" }
)
.then((response) => {
return new Promise((resolve, reject) => {
response.data
.on("end", () => {
resolve("\nDone");
})
.on("error", (err) => {
reject(err);
})
.on("data", (d) => {
progress += d.length;
if (process.stdout.isTTY) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(`Download ${progress} bytes`);
}
})
.pipe(dest);
});
});
}
Reference:
Drive v3 API Samples
You can see the sample script at download.js.

Google Drive v3. Nodejs Resumable Upload creates untitled file in wrong directory

Hey I have followed the steps from the Drive quick start guides and generated a token.json using the following scopes:
const SCOPES = [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.file'
];
I am then running the following code to try and create/upload a file to google drive.
async function testing() {
let driveFolder = 'FOLDERID';
let res;
try {
const oauth2Client = new google.auth.OAuth2(
credentials.client_id,
credentials.client_secret,
credentials.redirect_uris
);
await oauth2Client.setCredentials({
...tokenInfo
});
console.log('=== completed drive authentication', oauth2Client);
const drive = await google.drive({
version: 'v3',
auth: await oauth2Client
});
console.log('=== Initialised Drive API');
res = await drive.files.create({
parameters :{
uploadType: 'resumable'
},
requestBody: {
name: 'Testing.txt',
mimeType: 'text/plain',
parents: [driveFolder]
},
media: {
mimeType: 'text/plain',
body: 'Hello world!!!'
}
});
console.log('=== Completed Report UPLOAD:', res);
return;
} catch (err) {
console.error('ERROR: \n', err);
}
}
The request completes without catching an error but the response that returns is undefined.
The last two logs show the following
=== completed drive authentication OAuth2Client {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
transporter: DefaultTransporter {},
credentials:
{ access_token:
'ACCESS TOKEN STRING',
refresh_token:
'REFRESH TOKEN STRING',
scope:
'https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.appdata',
token_type: 'Bearer',
expiry_date: 1551070810157 },
certificateCache: null,
certificateExpiry: null,
refreshTokenPromises: Map {},
_clientId:'CLIENT ID STRING VALUE',
_clientSecret: 'CLIENT SECRET VALUE',
redirectUri: [ 'REDIRECT URL', 'http://localhost' ],
authBaseUrl: undefined,
tokenUrl: undefined,
eagerRefreshThresholdMillis: 300000 }
=== Initialised Drive API
=== Completed Report UPLOAD: undefined
You can try the quickstart using NodeJS from the documentation:
// change the scopes
const SCOPES = [ 'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.file'];
function uploadFile(auth){
var drive = google.drive('v3');
var fileMetadata = {
'name': 'Some.jpg' // name of the file
};
var media = {
mimeType: 'image/jpeg',
resumable: true,
//PATH OF THE FILE FROM YOUR COMPUTER
body: fs.createReadStream('/usr/local/google/home/malicdemj/Desktop/blue-cyber-future-technology-concept-background_42077-488.jpg')
};
drive.files.create({
auth: auth,
resource: fileMetadata,
media: media,
fields: 'id'
}, function (err, file) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('File Id: ', file.data.id);
}
});
}
Uploaded file in my drive:
Also, please check this SO post for more details.

How to get progress status while uploading files to Google Drive using NodeJs?

Im trying to get progress status values while uploading files to google Drive using nodeJs.
controller.js
exports.post = (req, res) => {
//file content is stored in req as a stream
// 1qP5tGUFibPNaOxPpMbCQNbVzrDdAgBD is the folder ID (in google drive)
googleDrive.makeFile("file.txt","1qP5tGUFibPNaOxPpMbCQNbVzrDdAgBD",req);
};
googleDrive.js
...
makeFile: function (fileName, root,req) {
var fileMetadata = {
'name': fileName,
'mimeType': 'text/plain',
'parents': [root]
};
var media = {
mimeType: 'text/plain',
body: req
};
var r = drive.files.create({
auth: jwToken,
resource: fileMetadata,
media: media,
fields: 'id'
}, function (err, file) {
if (err) {
// Handle error
console.error(err);
} else {
// r => undefined
console.log("Uploaded: " + r);
}
});
},
...
i followed this link but got always an undefined value
How about this modification?
Modification point:
It used onUploadProgress.
Modified script:
makeFile: function (fileName, root,req) {
var fileMetadata = {
'name': fileName,
'mimeType': 'text/plain',
'parents': [root]
};
var media = {
mimeType: 'text/plain',
body: req
};
var r = drive.files.create({
auth: jwToken,
resource: fileMetadata,
media: media,
fields: 'id'
}, {
onUploadProgress: function(e) {
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(e.bytesRead.toString());
},
}, function (err, file) {
if (err) {
// Handle error
console.error(err);
} else {
console.log("Uploaded: " + file.data.id);
}
});
},
Note:
If you want to show the progression as "%", please use the file size.
It was confirmed that this script worked at googleapis#33.0.0.
References:
axios
test of google/google-api-nodejs-client
In my environment, I'm using the script like above. But if this didn't work in your environment and if I misunderstand your question, I'm sorry.

Node.js: Bad Request when exporting Google Doc with REST API

I'm trying to export multiple Google Docs from a folder with the Drive REST Api. In the following code, 'fileId' is set to a specific fileId (and it works perfectly!):
var service = google.drive('v3');
fileId = <file_id>
dest = <local_file>
service.files.export({
fileId: fileId,
mimeType: 'text/plain',
auth: <auth>
})
.pipe(dest);
However, when I nest this in a loop in order to export multiple files like so:
var service = google.drive('v3');
fileId = <file_id>
dest = <local_file>
service.files.list({
auth: <auth>,
pageSize: 1,
fields: "nextPageToken, files(id)"
}, function(err, resp) {
if (err) { return err; }
var files = resp.files
for (var file of files) {
var fileId = file.id;
service.files.export({
fileId: fileId,
mimeType: 'text/plain',
auth: auth
})
.pipe(dest);
}
});
I'm given the following error:
{ [Error: Bad Request]
code: 400,
errors:
[ { domain: 'global',
reason: 'badRequest',
message: 'Bad Request' } ] }
And for the life of me I can't figure out why. Any help is much appreciated, Thanks!

Resources