Google Drive Api Files.Get giving weird response - node.js

I am trying to use the google drive api for storing images and retrieving them when necessary.
Uploading to drive through javascript i working fine.
The problem is when i do files.get() request for a particular file-id the response coming is having data in some weird format.(looking into header it is gzip encoding). Can someone suggest how can i convert this to base64 so that i can display the image in the front end.
code for uploading the file:
const uploadFile = async (fileObject) => {
const bufferStream = new stream.PassThrough();
bufferStream.end(fileObject.buffer);
const { data } = await driveService.files.create({
media: {
mimeType: fileObject.mimeType,
body: bufferStream,
},
requestBody: {
name: fileObject.originalname,
parents: [folderId],
},
fields: "id,name,webViewLink",
});
return data;
};
code for get request:
try{
const id = req.body.id;
driveService.files
.get({
fileId: id,
alt: "media",
})
.then(
function (response) {
res.status(200).json(response);
},
function (reason) {
throw reason;
}
);
} catch (e) {
console.error(e);
res.status(400).send(e.message);
}
the response i am getting is something like this:
Response Json for files.get request
I tried using Arraybuffers and then toString('base64'). but it did'nt helped.

There is a option to set response type while making the request.
The following changes solved the issue:
driveService.files.get(
{
fileId: id,
alt: "media",
},
{
responseType: "arraybuffer",
encoding: null,
},
function (err, response) {
if (err) throw err;
else {
var imageType = response.headers["content-type"];
var base64 = Buffer.from(response.data, "utf8").toString("base64");
var dataURI = "data:" + imageType + ";base64," + base64;
res.status(200).json({ src: dataURI });
}
now if i place this dataURI in src of an img element it works!!

Related

Error sending file from Slack to WhatsAPp

I am trying to send files from Slack to WhatsApp using Node.js. For Slack, I am using Bolt and for WhatsApp API I'm using WATI.
This is the code that retrieves the file information as soon as it is uploaded to slack. I am retrieving the file's permalink and passing it to the load function. I tried using file permalink_public it still gives the same error.
index.js
const wa = require("./wati")
const request = require('request-promise')
url = file_permalink
async function load(uri, path, number) {
const options = {
uri: uri,
encoding: null,
};
const body = await request(options)
try {
await wa.sendMedia(body, path, number).then().catch(e => console.log(e))
}
catch (e) {
console.log(e)
}
}
load(url, "file.png", phone_number)
wati.js
var request = require('request');
const sendMedia = async (file, filename, senderID) => {
var options = {
'method': 'POST',
'url': 'https://' + process.env.URL + '/api/v1/sendSessionFile/' + senderID,
'headers': {
'Authorization': process.env.API,
},
formData: {
'file': {
'value': file,
'options': {
'filename': filename,
'contentType': null
}
}
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
}
Error
{"result":false,"info":"Failed to send file"}
I tried with axios as well but it throws
TypeError: Cannot read properties of undefined (reading 'name')
index.js
let body = await axios.get(url, {
responseType: 'stream',
}).then(async () => {
// console.log(body)
}).catch(function (error) {
if (error.response) {
// Request made and server responded
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
});
await wa.sendMedia(body.data, "file.png", phone_number).then().catch(e => {
console.log(e)
})

Unable to successfully upload a video to AWS S3

I'm a bit lost on the way a video is being send from React Native to the backend and having it working on S3. All help would be appreciated especially where I might be going wrong.
Initially, from React Native I use the Expo Camera to record a video. Once it has stopped recording, we use fetch to send the data as follows:
const startRecording = async () => {
setIsRecording(true);
const video = await camera.recordAsync({
maxDuration: 15
});
const data = new FormData();
data.append('video', {
name: 'mobile-video-upload',
uri: video.uri
});
try {
const res = await fetch('url/users/testing', {
method: 'post',
body: data
});
} catch (error) {
console.log('error uploading');
}
};
the type of Data we get back from Camera component through IOS is:
Object {
"uri": "...Camera/D3B7B5F5-6A17-4C45-A0BE-897956A9E637.mov",
}
On the backend I'm using a middleware known as multer. My route for the backend looks like this
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
router.post('/testing', upload.single('video'), async (req, res) => {
let buffer = null;
fs.readFile(req.file.path, (err, data) => {
if (err) {
console.log('There was an error reading file: ', err);
}
buffer = data;
});
const params = {
Bucket: bucket_name,
Key: 'testing124.mov',
ContentType: req.file.mimetype,
Body: buffer
};
try {
let uploadPr = await s3.putObject(params).promise();
console.log(uploadPr);
} catch (error) {
console.log('There was an err ', error);
}
The data we see in req.file is:
{
fieldname: 'video',
originalname: 'mobile-video-upload',
encoding: '7bit',
mimetype: 'video/quicktime',
destination: 'uploads/',
filename: 'aed58f2dfbcc8daa7964fb3df7d3b4f4',
path: 'uploads/aed58f2dfbcc8daa7964fb3df7d3b4f4',
size: 480422
}
What might I be doing wrong in order to have a valid video uploaded? I'm unable to view the video from s3 whether I download the file or try using the link and viewing the video.
Thank you for all the help.
If your already using multer, use this:
https://www.npmjs.com/package/multer-s3

Downloading an image from Drive API v3 continuously gives corrupt images. How should I decode the response from the promise?

I'm trying to download images from a Google share Drive using the API v3. The download itself will succeed but the image can't be seen. Opening the image from the MacOS finder just results in a spinner.
I started using the example from the documentation (here: https://developers.google.com/drive/api/v3/manage-downloads):
const drive = google.drive({version: 'v3', auth});
// ....
var fileId = '0BwwA4oUTeiV1UVNwOHItT0xfa2M';
var dest = fs.createWriteStream('/tmp/photo.jpg');
drive.files.get({
fileId: fileId,
alt: 'media'
})
.on('end', function () {
console.log('Done');
})
.on('error', function (err) {
console.log('Error during download', err);
})
.pipe(dest);
however that fails because the .on() method doesn't exist. The exact error is "TypeError: drive.files.get(...).on is not a function"
The .get() method returns a promise. The response of the promise contains data that, depending on the config is either a stream, a blob or arraybuffer. For all options, when I write the response data to a file, the file itself becomes unviewable and has the wrong size. The actual code (typescript, node.js) for the arraybuffer example is below. Similar code for blob (with added name and modifiedDate) and for stream give the same result.
const downloader = googleDrive.files.get({
fileId: file.id,
alt: 'media',
}, {
responseType: 'arraybuffer',
});
return downloader
.then((response) => {
const targetFile = file.id + '.' + file.extension;
fs.writeFileSync(targetFile, response.data);
return response.status;
})
.catch((response) => {
logger.error('Error in Google Drive service download: ' + response.message);
return response.message;
}
);
}
So the questions are:
what is the correct way to handle a download through Google Drive API v3 ?
do I need to handle any formatting of the response data ?
All help greatly appreciated!
Thanks
You want to download a file from Google Drive using googleapis with Node.js.
You have already been able to use Drive API.
If my understanding is correct, how about this answer?
Pattern 1:
In this pattern, arraybuffer is used for responseType.
Sample script:
const drive = google.drive({ version: "v3", auth });
var fileId = '###'; // Please set the file ID.
drive.files.get(
{
fileId: fileId,
alt: "media"
},
{ responseType: "arraybuffer" },
function(err, { data }) {
fs.writeFile("sample.jpg", Buffer.from(data), err => {
if (err) console.log(err);
});
}
);
In this case, Buffer.from() is used.
Pattern 2:
In this pattern, stream is used for responseType.
Sample script:
const drive = google.drive({ version: "v3", auth });
var fileId = '###'; // Please set the file ID.
var dest = fs.createWriteStream("sample.jpg");
drive.files.get(
{
fileId: fileId,
alt: "media"
},
{ responseType: "stream" },
function(err, { data }) {
data
.on("end", () => {
console.log("Done");
})
.on("error", err => {
console.log("Error during download", err);
})
.pipe(dest);
}
);
Note:
If an error occurs, please use the latest version of googleapis.
From your question, it seems that you have already been able to retrieve the file you want to download using your request, while the file content cannot be opened. But if an error occurs, please try to add supportsAllDrives: true and/or supportsTeamDrives: true in the request.
References:
Download files
google-api-nodejs-client/samples/drive/download.js
If I misunderstood your question and this was not the direction you want, I apologize.
Posting a third pattern for completeness using async/await and including teamdrive files.
async function downloadFile(drive: Drive, file: Schema$File, localDir: string = "/tmp/downloads") {
if (!fs.existsSync(localDir)) {
fs.mkdirSync(localDir)
}
const outputStream = fs.createWriteStream(`${localDir}/${file.name}`);
const { data } = await drive.files.get({
corpora: 'drive',
includeItemsFromAllDrives: true,
supportsAllDrives: true,
fileId: file.id,
alt: "media",
}, {
responseType: 'stream',
})
await pipeline(data, outputStream)
console.log(`Downloaded file: ${localDir}/${file.name}`)
}
If someone is looking for a solution is 2023, here you go!
const downloadFile = async (file) => {
const dirPath = path.join(process.cwd(), '/images');
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
const filePath = `${dirPath}/${file.name}.jpg`;
const destinationStream = fs.createWriteStream(filePath);
try {
const service = await getService();
const { data } = await service.files.get(
{ fileId: file.id, alt: 'media' },
{ responseType: 'stream' }
);
return new Promise((resolve, reject) => {
data
.on('end', () => {
console.log('Done downloading file.');
resolve(filePath);
})
.on('error', (err) => {
console.error('Error downloading file.');
reject(err);
})
.pipe(destinationStream);
});
} catch (error) {
throw error;
}
};

Can't properly fetch GridFS image on Angular app

I'm really a newbie with Angular, MongoDB, so I'm asking for your help !
I'm trying to display images on an Angular App
Images are saved using Gridfs and sent to the app with fs.createReadStream
//The model
const ProfilePic = mongoose.Schema(
{
_id : mongoose.Schema.Types.ObjectId,
lenght: Number,
chunksize: Number,
uploadDate: Date,
filename: String,
md5: String,
contentType: String
}
);
// Create storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
const filename = file.originalname;
const fileInfo = {
filename: filename,
bucketName: "ProfilePic"
};
resolve(fileInfo);
});
}
});
const upload = multer({ storage });
router.post("/setProfilePic", upload.single("picture"), (req, res) => {
console.log("image received");
return res.status(200).json({err: "no"})
});
router.get('/getProfilePic/:filename', (req, res) => {
gfs.collection('ProfilePic'); //set collection name to lookup into
/** First check if file exists */
gfs.files.find({filename: req.params.filename}).toArray(function(err, files){
if(!files || files.length === 0){
return res.status(404).json({
responseCode: 1,
responseMessage: "error"
});
}
// create read stream
var readstream = gfs.createReadStream({
filename: files[0].filename,
root: "ProfilePic"
});
// set the proper content type
res.set('Content-Type', files[0].contentType)
// Return response
return readstream.pipe(res);
});
});
// method used to fetch the image
async getProfilePic(name) {
return this.http.get(this.url + "user/getProfilePic/" + name);
}
Here's the error I get on the application :
Unexpected token � in JSON at position 0
What is wrong with my code ?
The client/browser is trying to parse the response as json since that's the default. Just pass an object with a responseType attribute set to 'blob' and it should work.
// method used to fetch the image
async getProfilePic(name) {
return this.http.get(this.url + "user/getProfilePic/" + name, {responseType: 'blob'});
}
https://angular.io/api/common/http/HttpRequest#responseType

How to add item with image with react-redux-saga with nodejs/multer

I am trying to save an item that has images in the form. I was able to save the items using postman, but getting issue when trying from react-redux-saga. I am using ant design for the form.
Front-end code:
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
const { fileList } = this.state;
const formData = new FormData();
fileList.forEach(file => {
formData.append("files[]", file);
});
const postItemData = { ...values, images: formData };
this.props.createItem(postItemData);
}
});
};
saga:
When I try to console, I can get the item details with the image field as a formdata
{
let itemData = yield select(makeSelectPostItemData());
const token = yield select(makeSelectToken());
console.log(itemData, "itemData");
const response = yield call(request, `/item`, {
method: "POST",
headers: {
Authorization: `${token}`
},
body: JSON.stringify(itemData)
});
const successMessage = "Item saved successfully!";
yield put(postItemSuccess(response, successMessage));
}
nodejs:
const upload = multer({
storage: storage,
limits: { fileSize: 1000000 },
fileFilter: function(req, file, cb) {
checkFileType(file, cb);
}
}).array("images");
upload(req, res, err => {
if (err) {
console.log(err, "error");
res.send({ msg: err });
} else {
console.log(req, "req");
if (req.files === undefined) {
res.send({ msg: "Error: No file selected!" });
} else {
const { errors, isValid } = validatePostItem(req.body);
// check validation
if (!isValid) {
return res.status(400).json(errors);
}
const files = req.files;
}
}
});
On the last line const files = req.files. When I try from postman, I get file details, but when I try to request it from react, I have no req.files. How can I get the req.files from the react as I am getting from postman?
I have found the solution for this. In the handleSubmit, I have following code:
fileList.forEach(file => {
formData.append("files[]", file);
});
const postItemData = { ...values, images: formData };
this.props.createItem(postItemData);
Instead of spreading values and inserting the formData in images, what I did was put the all of them into formData.
fileList.forEach(file => {
formData.append("files[]", images);
});
formData.append("name", values.name);
formData.append("price", values.price);
formData.append("detail", values.detail);
this.props.createItem(formData);
With this, I could get req.files and req.body.name, req.body.price, req.body.detail in the node side. Quite different from the postman, but this worked for me.

Resources