Send image to Google Drive API from NodeJS - node.js

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.

Related

Unable to upload image file to s3 from React with Node presigned url

I am trying to upload an image file to an AWS S3 bucket. I am using Node/Express to generate a presigned URL to upload said image directly to s3 from the React frontend. The thing is I am able to upload the file as a file (same size as on local PC) but it does not have the file extension. That is, it has only the signed character e.g. 9c9743b9-4dd7-4afa-af06-c060fb0fb175 with file type of file instead of 9c9743b9-4dd7-4afa-af06-c060fb0fb175.png with png file type
Here is a brief snippet of what I have tried so far
Backend
// My controller
try {
const payload = await AwsS3Service.getSignedUrlService();
res.status(200).send({
success: true,
data: payload
});
...
const s3 = new aws.S3({
region: config.awsS3.region,
accessKeyId: config.awsS3.accessKeyId,
secretAccessKey: config.awsS3.secretAccessKey,
signatureVersion: config.awsS3.signatureVersion
})
const getSignedUrlService = async () => {
const imageName = crypto.randomUUID()
const params = ({
Bucket: config.awsS3.bucketName,
Key: imageName,
Expires: 120 // in sec
})
const uploadUrl = await s3.getSignedUrlPromise('putObject', params)
return uploadUrl
}
Frontend
const [final, setFinal] = useState<FileList | any>('')
const [img, setImg] = useState<typeof formState.file>(null)
const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files && e.target.files[0]
if (e.target.files) {
setFinal(e.target.files[0]!)
}
const { type, name } = file as File
setImgName(name)
setImgType(type)
const fileReader = new FileReader()
fileReader.onload = (e) => {
setImg(e.target?.result)
}
fileReader.readAsDataURL(file as Blob)
}
const handleFormSubmit: SubmitHandler<IImage> = async (data: IImage) => {
try {
const signedUrl = await axios({ method: 'GET', url: `${config.API_URL}/awss3` })
const formData = new FormData()
formData.append('file', final[0])
const resUpload = await fetch(`${signedUrl.data.data}`, {
method: 'PUT',
headers: { 'Content-Type': 'multipart/form-data' },
body: final
})
if (resUpload.status === 200) {
// redirect
}
} catch(err: any) {
alert(`An error occured. ${err.message}`)
}
}
return (
<form onSubmit={e => e.preventDefault()}>
<Stack>
<Box>
<InputLabel htmlFor='imgfile'>Image File</InputLabel>
<picture>
{ img
? <img src={img} alt='Image preview' height={90} width={160} />
: <Box sx={{ height: 90, width: 160, backgroundColor: '#555' }}></Box>
}
</picture>
<input type='file' accept='image/*' id='imgfile' name='imgFile' onChange={handleImageChange} />
</Box>
</Stack>
<Button type='submit' onClick={handleSubmit(handleFormSubmit)}>
Submit
</Button>
</form>
)
I have been unable to solve this for a while now. Any help is much appreciated. Thank you for reading.
Posting this as a self-answer just in case anyone is interested.
I managed to work around this by adding the file name, type, and extension when making the sign request as a POST request instead and attaching the file name and file type in the request body.
// Frontend
const signedUrl = await axios({
method: 'POST',
url: `${config.API_URL}/awss3`,
headers: { 'Content-Type': 'application/json' },
data: { name: imgName, type: imgType }
})
And added them in the Key
// Backend
const getSignedUrlService = async (name, type) => {
// name and type from req.body
const imageName = crypto.randomUUID() + '-' + name
const params = ({
Bucket: config.awsS3.bucketName,
Key: imageName,
ContentType: type,
Expires: 120 // in sec
})
const uploadUrl = await s3.getSignedUrlPromise('putObject', params)
return uploadUrl
}
Thanks to everyone who helped

How to upload a file in a folder GOOGLE DRIVE API

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

React.js download pdf file from node backend

I have code that just downloads a file that exists in my backend. I can see the pdf on the backend is created properly and in the right place, but when I send and download the file to the frontend and open it I get the error "Failed to load pdf document" no matter which browser I use. I think this must mean there is something wrong with my blob downloading code since I can open and see the file on the backend, but I can't figure it out. I have tried following many examples online and I get the same issue regardless of what I have tried.
server.js (node backend file)
app.get('/api/v1/getPdf', function(req, res) {
let resolve = require('path').resolve
res.sendFile(resolve('./tickets/tickets.pdf'));
});
PrintDetails.js (React js code for downloading pdf) - Note: I only included the relevant parts
class PrintDetails extends React.Component {
async printTickets() {
let file = await getTicketsPdf();
console.log(file);
const blob = new Blob([file], {type: 'application/pdf'});
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'tickets.pdf';
document.body.appendChild(link);
link.click();
setTimeout(function() {
document.body.removeChild(link);
}, 100);
}
render() {
return (
<div>
<button className="download-button-icon" onClick={this.printTickets}>Download</button>
</div>
)
}
async function getTicketsPdf() {
let data = {};
await (async () => {
const rawResponse = await fetch('/api/v1/getPdf', {
method: 'get',
headers: {
'responseType': 'blob',
'Content-Type': 'application/json'
},
});
data = await rawResponse;
})();
return data;
}
Here's my implementation using axios and file-saver.
Node.js backend
app.get('/api/v1/getPdf', function(req, res) {
res.download('./tickets/tickets.pdf');
});
React frontend
import { saveAs } from 'file-saver'
.
.
.
async function printTickets() {
const { data } = await getTicketsPdf()
const blob = new Blob([data], { type: 'application/pdf' })
saveAs(blob, "tickets.pdf")
}
async function getTicketsPdf() {
return axios.get('/api/v1/getPdf', {
headers: {
'Content-Type': 'multipart/form-data'
},
responseType: 'arraybuffer'
})
}

PDF not uploading to Amazon S3 with Nodejs and Reactjs

I am working on uploading PDF files to S3 using nodejs and react and I am running into an issue where some PDFs are being uploaded and some are not.
This endpoint gets a signed url from AWS
'/api/v1/upload/pdf',
requireAuth,
roleAuthorization(['admin']),
(req, res) => {
const date = new Date();
const year = date.getFullYear();
const key = `${date.toLocaleString('default', {
month: 'long',
})}-${short.uuid('0123456789').slice(0, 2)}-${year}.pdf`;
s3.getSignedUrl(
'putObject',
{
Bucket: 'bucket-name',
ContentType: 'application/pdf',
Key: key,
},
(err, url) => res.send({ key, url })
);
}
);
And this endpoint does the upload from react js and save the link to the file in the database
const createIssue = (dispatch) => async (issue, town) => {
try {
const token = await localStorage.getItem('token');
const uploadConfig = await axios.get('/api/v1/upload/pdf', {
headers: {
Authorization: `Bearer ${token}`,
},
});
const upload = await axios.put(uploadConfig.data.url, issue, {
headers: {
'Content-Type': issue.type,
},
});
const response = await api.post(`/api/v1/town/${town._id}/issue`, {
issue: uploadConfig.data.key,
});
dispatch({ type: 'create_issue', payload: response.data });
} catch (err) {
dispatch({
type: 'add_error',
payload: err.message,
});
}
};
This works but does not work for all PDF files, the file remains pending and does not upload.
Any help welcome.
thanks

google drive api upload file after created new folder

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'),
}

Resources