How to use Google Drive API to download files with Javascript - javascript

I want to download files from google drive with javascript API. I have managed to authenticate and load list of files using gapi.client.drive.files request. However, I stuck at downloading those files.
My attempt to download the file:
var request = gapi.client.drive.files.get({
fileId:id,
alt:'media'
});
request.execute(function(resp){
console.log(resp);
});
I have these errors when trying to run the above:
(403) The user has not granted the app 336423653212 read access to the file 0B0UFTVo1BFVmeURrWnpDSloxQlE.
(400) Bad Request
I recognize that the files which aren't google drive file (google doc, google slide) return the 403 error.
I am new to this. Any help and answer is really appreciated.
Update 0
From Google Drive documentation about Handling API Error, here is part of the explanation for 400 errors
This can mean that a required field or parameter has not been provided, the
value supplied is invalid, or the combination of provided fields is
invalid.
This is because I have alt:'media' in my parameter object.
I tried gapi.client.drive.files.export, but it doesn't work either and it returns (403) Insufficient Permission although my Google Drive account has the edit permission for those files. Here is my code:
var request = gapi.client.drive.files.get({
fileId:element.id,
});
request.then(function(resp){
console.log(resp.result);
type = resp.result.mimeType;
id = resp.result.id;
var request = gapi.client.drive.files.export({
fileId:id,
mimeType:type
})
request.execute(function(resp){
console.log(resp);
});
});
Update 1
Based on abielita's answer, I have tried to make an authorized HTTP request but it doesn't download the file. It actually returns the file information in response and responseText attribute in the XMLHttpRequest object.
function test() {
var accessToken = gapi.auth.getToken().access_token;
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.googleapis.com/drive/v3/files/"+'1A1RguZpYFLyO9qEs-EnnrpikIpzAbDcZs3Gcsc7Z4nE', true);
xhr.setRequestHeader('Authorization','Bearer '+accessToken);
xhr.onload = function(){
console.log(xhr);
}
xhr.send('alt=media');
}
______________________________________________________
I found out that I can actually retrieve URLs of all those files from the folder using files' webViewLink or webViewContent attributes.
A file which is from Google Drive type (Google Doc, Google Sheet,
etc...) will have webViewLink attribute. A webViewLink will open
the file in Google Drive.
A non Google Drive type file will have webContentLink. A
webContentLink will download the file.
My code:
var request = gapi.client.drive.files.list({
q:"'0Bz9_vPIAWUcSWWo0UHptQ005cnM' in parents", //folder ID
'fields': "files(id, name, webContentLink, webViewLink)"
});
request.execute(function(resp) {
console.log(resp);
}

Based from this documentation, if you're using alt=media, you need to make an authorized HTTP GET request to the file's resource URL and include the query parameter alt=media.
GET https://www.googleapis.com/drive/v3/files/0B9jNhSvVjoIVM3dKcGRKRmVIOVU?alt=media
Authorization: Bearer ya29.AHESVbXTUv5mHMo3RYfmS1YJonjzzdTOFZwvyOAUVhrs
Check here the examples of performing a file download with our Drive API client libraries.
String fileId = "0BwwA4oUTeiV1UVNwOHItT0xfa2M";
OutputStream outputStream = new ByteArrayOutputStream();
driveService.files().get(fileId)
.executeMediaAndDownloadTo(outputStream);
For the error (403) Insufficient Permission, maybe this is a problem with your access token, not with your project configuration.
The insufficient permissions error is returned when you have not requested the scopes you need when you retrieved your access token. You can check which scopes you have requested by passing your access_token to this endpoint: https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ACCESS_TOKEN
Check these links:
google plus api: "insufficientPermissions" error
Google drive Upload returns - (403) Insufficient Permission
Remember you are uploading to the service accounts google drive account. If you want to be able to see it from your own Google drive account you are going to have to do an insert of the permissions. to give yourself access

Phu, you were so close!
Thank you for sharing your method of using the webContentLink and webViewLink. I think that is best for most purposes. But in my app, I couldn't use viewContentLink because need to be able to enter the image into a canvas, and the image google provides is not CORS ready.
So here is a method
var fileId = '<your file id>';
var accessToken = gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse().access_token;// or this: gapi.auth.getToken().access_token;
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.googleapis.com/drive/v3/files/"+fileId+'?alt=media', true);
xhr.setRequestHeader('Authorization','Bearer '+accessToken);
xhr.responseType = 'arraybuffer'
xhr.onload = function(){
//base64ArrayBuffer from https://gist.github.com/jonleighton/958841
var base64 = 'data:image/png;base64,' + base64ArrayBuffer(xhr.response);
//do something with the base64 image here
}
xhr.send();
Notice that I set the response type to arraybuffer, and moved alt=media up to the xhr.open call. Also I grabbed a function that converts the array buffer to base64 from https://gist.github.com/jonleighton/958841.

I found out that I can actually retrieve URLs of all those files from the folder using files' webViewLink or webViewContent attributes. A file which is of Google Drive type (Google Doc, Google Sheet, etc...) will have webViewLink attribute and a non Google Drive type file will have webContentLink. The webViewLink will open the file in Google Drive and the webContentLink will download the file. My code:
var request = gapi.client.drive.files.list({
q:"'0Bz9_vPIAWUcSWWo0UHptQ005cnM' in parents", //folder ID
fields: "files(id, name, webContentLink, webViewLink)"
});
request.execute(function(resp) {
console.log(resp); //access to files in this variable
}

Task: download the file and create File object;
Environment: browser;
const URL = 'https://www.googleapis.com/drive/v3/files';
const FIELDS = 'name, mimeType, modifiedTime';
const getFile = async (fileId) => {
const { gapi: { auth, client: { drive: { files } } } } = window;
const { access_token: accessToken } = auth.getToken();
const fetchOptions = { headers: { Authorization: `Bearer ${accessToken}` } };
const {
result: { name, mimeType, modifiedTime }
} = await files.get({ fileId, fields: FIELDS });
const blob = await fetch(`${URL}/${fileId}?alt=media`, fetchOptions).then(res => res.blob());
const fileOptions = {
type: mimeType,
lastModified: new Date(modifiedTime).getTime(),
};
return new File([blob], name, fileOptions);
};

I was able to download using the files.get API:
var fileId = '<your file id>';
gapi.client.drive.files.get(
{fileId: fileId, alt: 'media'}
).then(function (response) {
// response.body has the file data
}, function (reason) {
alert(`Failed to get file: ${reason}`);
});

let url = https://drive.google.com/uc?id=${file_id}&export=download;
Make sure to pass the file_id in this link.
You can get the file id from the file you want to download by getlink --> general access. Make sure the file is public.

Related

Cloud Storage for Firebase: How to recover a pdf

I have a pdf stored in Cloud Storage and I'm trying to take this file to send it through email.
I'm trying to recover it but I receive back an error about access deniend:
Uncaught (in promise): FirebaseError: Firebase Storage: User does not
have permission to access
My code:
const storageRef = firebase.storage().ref();
var forestRef = storageRef.child('/uploads/' + offerId + '/' + offerId + '.pdf');
forestRef.getDownloadURL()
.then(function (url) {
console.log("url ", url)
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function (event) {
var blob = xhr.response;
};
xhr.open('GET', url);
})
I think that the problem should be that I'm not using the access token but I don't know how to recover it. ( I have tried to use also the getMetadata, but the result is the same)
Edit:
I have also the url with token
The files in firebase storage follow a specific url format. use the following format. The url generated from getDownloadURL() will have token associated with it causing the link to expire after few days.
https://firebasestorage.googleapis.com/v0/b/<PROJECT-NAME>.appspot.com/o/<PATH>%2F<TOFILE>?alt=media
So your url string for /uploads/${offerId}/${offerId}.pdf will be :
https://firebasestorage.googleapis.com/v0/b/<PROJECT-NAME>.appspot.com/o/uploads%2F${offerId}%2F${offerId}.pdf?alt=media
Thus by string manipulations you can create the file urls.
While download URLs provide public, read-only access to files in Cloud Storage for Firebase, calling getDownloadURL to generate such a URL requires that you have read permission on that file.
The error message indicates that the code does not meet your security rules, i.e. that there is no user signed in when you run this code.
If that is not what you expect, I recommend checking that right before you call the Storage API:
const storageRef = firebase.storage().ref();
var forestRef = storageRef.child('/uploads/' + offerId + '/' + offerId + '.pdf');
if (!firebase.auth().currentUser) throw new "No user signed in, can't get download URL";
forestRef.getDownloadURL()
...

How can I upload files to google drive that are in a url?

I try to upload a photo that I have in a URL on another server, but it does not work for me or I do not know how to upload them in this case I am going to upload a photo but I also want to upload files that will upload to that URL.
const img = await fetch("http://example.com/api/photo")
await gapi.client.drive.files.create({
resource: {
name: "New Folder",
body: img,
}
})
The simple anwser is you cant do it like that. The file being Uploaded must be sent in the form of a stream
Download the file to your own machine and then upload it from there. Or try to figure out how to turn your url into a stream.
var fileMetadata = {
'name': 'photo.jpg'
};
var media = {
mimeType: 'image/jpeg',
body: fs.createReadStream('files/photo.jpg')
};
drive.files.create({
resource: fileMetadata,
media: media,
fields: 'id'
}, function (err, file) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('File Id: ', file.id);
}
});
I believe your goal as follows.
You want to download an image data from an URL, and want to upload the downloaded image data to Google Drive.
From your script, the image data is downloaded by const img = await fetch("http://example.com/api/photo").
You want to achieve this using googleapis for Javascript.
Modification points:
In this case, it retrieves Blob of image data from fetch, and the blob is uploaded to Google Drive.
Unfortunately, in the current stage, it seems that although googleapis for Javascript can create new file with the metadata, the file content cannot be included. By this, in this answer, I use the method of this thread. The downloaded image data is uploaded using fetch with multipart/form-data.
When above poiints are reflected to your script, it becomes as follows.
Modified script:
const img = await fetch("http://example.com/api/photo").then((e) => e.blob());
const fileMetadata = {name: "sampleName"}; // Please set filename.
const form = new FormData();
form.append('metadata', new Blob([JSON.stringify(fileMetadata)], {type: 'application/json'}));
form.append('file', img);
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', {
method: 'POST',
headers: new Headers({'Authorization': 'Bearer ' + gapi.auth.getToken().access_token}),
body: form
}).then(res => res.json()).then(res => console.log(res));
By this modification, the downloaded image data is uploaded to Google Drive with multipart/form-data.
Note:
In this modification, it supposes as follows.
Your URL of http://example.com/api/photo is the direct link of the image data.
Your authorization script can be used for uploading a file to Google Drive.
In this answer, as a sample script, the file is uploaded with uploadType=multipart. In this case, the maximum file size is 5 MB. Please be careful this. When you want to upload the file with the large size, please check the resumable upload. Ref
References:
Google API Client Library for JavaScript
Using Fetch
Files: create
Upload file data
Related question
How I can upload file to google drive with google drive api?

How to restrict re-uploading after uploading of file using singed url in google cloud store in Node.js?

I am able to create siged url for uploading file to google cloud store with help of example given at
https://github.com/googleapis/nodejs-storage/blob/master/samples/generateV4UploadSignedUrl.js
var {Storage} = require('#google-cloud/storage')
var storage = new Storage({
projectId: "projectId",
credentials: {
client_email: "clientEmail",
private_key: "privateKey"
}
})
var generateUploadSignedUrl = async function(bucketName, remoteFilename, expires) {
const options = {
version: 'v4',
action: 'write',
expires: expires,
contentType: 'application/octet-stream',
}
var url = await storage.bucket(bucketName).file(remoteFilename).getSignedUrl(options)
return url
}
I am able to use singed url for uploading file.
But I want to put restriction that after uploading I should not be able to upload again. How can I add such policy?
I gave look on policy-document but could not find relevent condition for restricting re-upload.
This is not possible to do it.
If you use gsutil, you can use the flag -n. As this documentation says, "When specified, existing files or objects at the destination will not be overwritten. Any items that are skipped by this option will be reported as being skipped".
On the other hand, using the client library, there is nothing like that. Nevertheless, first, you can check if the file exists, and if not, copy it.

I keep getting a 403 from Firebase storge when trying to read image files

I'm having a hard time understanding the whole token part of Firebase uploads.
I want to simply upload use avatars, save them to the database and then read them at the client side.
const storageRef = firebase.storage().ref();
storageRef.child(`images/user-avatars/${user.uid}`).put(imageObj);
Then, in my cloud function, I grab the new url like this:
exports.writeFileToDatabase = functions.storage.object().onFinalize(object => {
const bucket = defaultStorage.bucket();
const path = object.name as string;
const file = bucket.file(path);
return file
.getSignedUrl({
action: "read",
expires: "03-17-2100"
})
.then(results => {
const url = results[0];
const silcedPath = path.split("/");
return db
.collection("venues")
.doc(silcedPath[1])
.set({ images: FieldValue.arrayUnion(url) }, { merge: true });
});
});
I've enabled IAM in the Google APIs platform, and have added Cloud functions service agent to the App Engine default service account.
I feel like the exact same configuration has worked before, butt now it sometimes doesn't even write the new url or I get 403 trying to read it. I can't find any explanations or errors to what I'm doing wrong.
EDIT:
Forgot to add this piece of code, but FieldValue is set at the top of the document as
const FieldValue = admin.firestore.FieldValue;
EDIT:
This the exact error I get Failed to load resource: the server responded with a status of 403 ()
And I just got it when I've tried to use this link, which has been generated automatically by the function above, as the source for an image component:
https://storage.googleapis.com/frothin-weirdos.appspot.com/images/user_avatars/yElCIVY4bAY5g5LnoOBhqN6mDNv2?GoogleAccessId=frothin-weirdos%40appspot.gserviceaccount.com&Expires=1742169600&Signature=qSqPuuY4c5xmdnpvfZh39Pw3Vyu2B%2FbGMD1rQwHDBUZTAnKwP11MaOFQt%2BTV53krkIgvJgQT0Xl3UUxkngmW9785fUri75SSPoBk0z4DKyZnEBLxgTGRE8MzmXadQ%2BHDJ3rSI8IkkoomdnANpLsPN9oySshZ1h4BfOBvAmK0hQ4Gge1glH7qhxFjVWfX3tovZoL8e2smhuCRXxDsZtJh0ihbIeZUEnX8lGic%2B9IT6y4OskS2ZlrZNjvM10hcEesoPdHsT4oCvfhCNbUcJcueRKfsWlDCd9m6qmf42WVOc7UI0nE0oEvysMutWY971GVRKTLwIXRnTLSNOr6fSvJE3Q%3D%3D

Google Drive API failing. Files:get response CORS error

I have a web application that relies on saving files to Google Drive and later retrieving them from the server. I am using the Google API Javascript Client and using similar code to the Google Drive API example to retrieve the file. I use a request to the Google API get service to retrieve the file metadata and then I use that file's downloadUrl contained in the metadata to request that file through an XmlHttpRequest, retrieving the access token from the Google API.
getContents = function(fileId,callbackFunction) {
var request = gapi.client.drive.files.get({ fileId : fileId});
request.execute(function(metadata) {
if(metadata.downloadUrl) {
var connection = new haxe.Http(metadata.downloadUrl);
var accessToken = gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse(true).access_token;
connection.setHeader("Authorization","Bearer " + accessToken);
connection.async = true;
connection.onData = function(data) {
callbackFunction(metadata,haxe.io.Bytes.ofString(data));
};
connection.onError = function(error) {
callbackFunction(metadata,null);
};
connection.request(false);
}
});
}
As recently as of yesterday, the Google API has stopped working when retrieving files. I can get the file metadata just fine, but when requesting the downloadUrl, it is blocked by the CORS policy because the Google API response does not contain the 'Access-Control-Allow-Origin' header. I have made no changes to the relevant code for a long time, so I am at a loss as to why this suddenly stopped working. Does anyone have any insight as to the source of this problem?

Categories

Resources