I'm currently following Google Drive APIs document and trying to create function create a folder then upload a file to it after creation. Currently it only managed to create new folder but the file i want to upload to it doesn't appear in that newly created folder.
I already enable to the api and shared the target folder with the service account
upload.js
const { google } = require('googleapis');
const fs = require('fs');
const key = require('./test-creds.json');
const drive = google.drive('v3');
const targetFolderId = "1Q_2I3_UpAGs13jEMXGYLgm0dXahnhN4Z"
const jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
['https://www.googleapis.com/auth/drive'],
null
);
function uploadFile(childFolderID) {
const fileMetadata = {
name: 'photo.jpg'
}
const media = {
mimeType: 'image/jpeg',
body: fs.createReadStream('photo.jpg'),
parents: [childFolderID]
}
drive.files.create({
auth: jwtClient,
resource: fileMetadata,
media: media,
fields: 'id'
}, (err, file) => {
if (err) {
console.log(err);
return;
} else {
console.log("imaged uploaded with id ", file.data.id);
console.log(childFolderID);
}
});
}
jwtClient.authorize((authErr) => {
if (authErr) {
console.log(authErr);
return;
}
const folderMetadata = {
name: 'child5',
mimeType: 'application/vnd.google-apps.folder',
parents: [targetFolderId]
};
drive.files.create({
auth: jwtClient,
resource: folderMetadata,
fields: 'id'
}, (err, file) => {
if (err) {
console.log(err);
return;
}
console.log('uploaded folder with id: ', file.data.id);
const childFolderID = file.data.id;
return uploadFile(childFolderID);
})
});
here the output:
uploaded folder with id: 1RKu9fxBr-6Pl7F0x5vfrqWb3cgH095BO imaged
uploaded with id 1QGBjXdv6GkgFQDtEsapA_hpAkXGRYEs7
any help would be appreciate :)
i found out what i did wrong the
parents: [childFolderID]
should be in fileMetadata because it's a file type so in fileUpload function it should be this:
const fileMetadata = {
name: 'photo.jpg',
parents: [childFolderID]
}
const media = {
mimeType: 'image/jpeg',
body: fs.createReadStream('photo.jpg'),
}
Related
Within my app a user can select a profile image and i would like that image to be uploaded to an s3 bucket when the user saves their profile data
I pass the image data (and json, which consists of name, email, telephone for example) from my app to an express server and upload there
At present I can pass the image data (the url it seems at present) to an s3 bucket and it saves
I don't think i'm actually saving the image itself though, as when downloading from s3 (manually) and trying to open on my mac it states it may be damaged and i cannot see the image
Feel daft for asking but how do i actually upload the image itself? Thanks
React Native Side
const handleFormSubmit = formData => {
const jsonData = JSON.stringify({
...formData,
});
// Handle profile image
if (imageProps && imageProps.uri) {
const data = new FormData();
data.append('formBody', jsonData);
data.append('image', {
uri:
Platform.OS === 'android'
? imageProps.uri
: imageProps.uri.replace('file://', ''),
type: imageProps.type,
name: imageProps.fileName,
});
sendRequest(data);
} else {
sendRequest(jsonData);
}
};
const sendRequest = data => {
let responseData;
fetch('http://localhost:8080/users/api/update_user_profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: data,
})
.then(response => {
responseData = response;
return response.json();
})
.then(jsonData => {
console.log(jsonData)
})
.catch(error => {
console.log(error)
});
};
Server Side
const s3 = new AWS.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});
// Setting up S3 upload parameters
const params = {
Bucket: 'bucket-folder',
ACL: 'public-read',
Key: req.files.image.name,
Body: req.files.image.path
};
const stored = await s3.upload(params).promise();
You can use Multer for uploading files to s3.
const multer = require('multer');
const AWS = require('aws-sdk');
const uniqid = require('uniqid');
const storage = multer.memoryStorage();
const upload = multer({ storage });
// ? Posts new file to amazon and saves to db
router.post(
'/:id',
upload.single('attachment'),
async (req, res) => {
const unique = uniqid.time();
const { file } = req;
const { filePath } = req.body;
const { id } = req.params;
const s3FileURL = process.env.AWS_UPLOADED_FILE_URL;
const region = process.env.AWS_REGION;
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
const Bucket = process.env.AWS_BUCKET_NAME + '/' + filePath;
const Key = `${id}/${unique}-${file.originalname}`;
const Body = file.buffer;
const ContentType = file.mimetype;
const ACL = 'public-read';
const s3bucket = new AWS.S3({
accessKeyId,
secretAccessKey,
region,
});
const params = {
Bucket,
Key,
Body,
ContentType,
ACL,
};
s3bucket.upload(params, async (err, data) => {
if (err) {
res.status(500).json({ error: true, Message: err });
} else {
console.log(params);
const newFileUploaded = {
description: req.body.description,
fileLink: `${s3FileURL}${filePath}/${id}/${unique}-${file.originalname}`,
s3_key: params.Key,
};
try {
const response = await postFile({
name: req.body.name,
attachment: newFileUploaded,
alt: req.body.alt,
user: req.body.user,
relatedID: req.body.relatedID,
});
res.status(200).json({
message: response.message,
success: response.success,
result: response.result,
});
} catch (e) {
res.status(500).json({
message:
'File upoladed but Db couldnt saved request (upload by ID)',
success: false,
result: [],
});
}
}
});
}
);
So i try this, i use my folder ID, but that's doesn't work, my files are create but they don't go in my folder. In the doc of Google Drive there is only this example. When i submit a form on Slack, that's push my data on a Google Spreadsheet and after that's auto generate a ticket on monday. Now i want to push my spreadsheet on a special folder in my Drive.. Can i provide my files in an other way ?
async function createSpreadsheet(filename, body) {
const ID_FOLDER_DRIVE = MY_ID;
try {
const fileMetaData = {
'name': filename,
parents: [ID_FOLDER_DRIVE]
}
const media = {
mimeType: 'text/csv',
body: body
}
const res = await drive.files.create({
requestBody: {
resource: fileMetaData,
mimeType: 'application/vnd.google-apps.spreadsheet'
},
media: media
})
return res.data
} catch (error) {
throw error
}
}
async function generatePublicUrl(id) {
console.log({ id })
try {
await drive.permissions.create({
fileId: id,
requestBody: {
role: 'reader',
type: 'anyone',
},
});
/*
webViewLink: View the file in browser
webContentLink: Direct download link
*/
const result = await drive.files.get({
fileId: id,
fields: 'webViewLink, webContentLink',
});
return result.data.webViewLink
} catch (error) {
throw error
}
}
In your situation, how about the following modification?
From:
const fileMetaData = {
'name': filename,
parents: [ID_FOLDER_DRIVE]
}
const media = {
mimeType: 'text/csv',
body: body
}
const res = await drive.files.create({
requestBody: {
resource: fileMetaData,
mimeType: 'application/vnd.google-apps.spreadsheet'
},
media: media
})
To:
const fileMetaData = {
name: filename,
parents: [ID_FOLDER_DRIVE],
mimeType: 'application/vnd.google-apps.spreadsheet'
}
const media = {
mimeType: 'text/csv',
body: body
}
const res = await drive.files.create({
requestBody: fileMetaData,
media: media
})
Reference:
google-api-nodejs-client
I managed to upload text files to google API with this code
google.drive({
version: 'v3',
auth
})
var media = {
mimeType: 'text/plain',
body: 'text'
}
drive.files.create({
media: media,
fields: 'id'
})
But if i try to upload an image as suggested in documentation i'm getting empty file on the drive.
Trying to do that this way (file exists and has all privileges)
const drive = google.drive({
version: 'v3',
auth
})
var media = {
mimeType: 'image/png',
body: fs.createReadStream(path.resolve(__dirname, '../assets/logo.png'))
}
drive.files.create({
media: media,
fields: 'id'
})
And when I overviewing request in debug console i see that there was no request body.
Please, help.
Below is the whole component which is doing the upload
<template>
<div>
<button class="btn btn-block btn-success mb-3" #click="connect">Syncronize</button>
<h3 class="text-center">
<template v-if="getToken">Sync success</template>
<template v-else>Sync failed</template>
</h3>
<button #click="tryUpload">Test API</button>
</div>
</template>
<script>
import OAuth2 from '../classes/OAuth2'
import { mapActions, mapGetters } from 'vuex'
const {google} = require('googleapis')
export default {
computed: {
...mapGetters([
'getToken'
])
},
methods: {
...mapActions([
'saveToken'
]),
connect () {
(async () => {
let token = await new OAuth2().getToken()
this.saveToken(token)
})()
},
tryUpload () {
const auth = new google.auth.OAuth2(
'....',
'.....',
'http://127.0.0.1:42813/callback'
)
auth.setCredentials(this.getToken)
const drive = google.drive({
version: 'v3',
auth
})
let stream = fs.createReadStream(path.resolve(__dirname, '../assets/logo.png'))
var media = {
mimeType: 'image/png',
body: stream
}
drive.files.create({
media: media,
fields: 'id'
}, function (err, file) {
if (err) {
console.error(err)
} else {
console.log('File Id: ', file.id)
}
})
}
}
}
</script>
Try importing the file with require instead
var media = {
mimeType: 'image/png',
body: require(path.resolve(__dirname, '../assets/logo.png'))
}
As it is, you are uploading the stream itself, not the file being streamed. If you want to use fs, you should try accessing the stream using the callback inside of the stream.on('{event}' function () {}) method.
It's working for me when I upload a file using arrow functions syntax and async/await (at least for me it's more comfortable like this):
// Your tryUpload Function
const tryUpload = async () => {
// ...Previous OAuth workflow
// Build Drive service
const drive = google.drive({version: 'v3', auth});
try {
const data = await uploadFile(drive);
console.log(data);
} catch(err){
console.log(`There was a problem in the promise ---> ${err}`);
}
}
const uploadFile = (drive) =>{
// Set file metadata and data
const fileMetadata = {'name': 'testupload.png'};
const media = {
mimeType: 'image/png',
// If it's in the same dir, just pass 'testupload.png'
body: fs.createReadStream('path/to/testupload.png')
};
// Return the Promise result after completing its task
return new Promise((resolve, reject) => {
// Call Files: create endpoint
return drive.files.create({
resource: fileMetadata,
media: media
},(err, results) => err ? reject(err) : resolve(results))
});
};
Docs
As a guide to help you. I used these docs:
Files: create.
Perform a simple upload.
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.
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!