This is my function to upload the file to google drive:
async processFiles(files) {
const formData = new FormData()
formData.append("file", files[0])
formData.append("name", files[0].name)
formData.append("parents", this.currentFolder.folderId)
axios
.post("https://www.googleapis.com/upload/drive/v3/files", formData, {
headers: {
Authorization: `Bearer ${this.accessToken}`,
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
console.log(response)
})
.catch((error) => {
console.log(error)
})
},
the file is uploading to the general google drive and not to the specific folder (this.currentFolder.folderId).
What am I doing wrong here?
I tried some functions already and this is the only one that uploads file to the google drive.
In your script, how about the following modification?
Modified script:
async processFiles(files) {
const formData = new FormData();
formData.append("file", files[0]);
formData.append('metadata', new Blob([JSON.stringify({ name: files[0].name, parents: [this.currentFolder.folderId] })], { type: 'application/json' }));
axios
.post("https://www.googleapis.com/upload/drive/v3/files", formData, {
headers: { Authorization: `Bearer ${this.accessToken}` },
})
.then((response) => {
console.log(response.data)
})
.catch((error) => {
console.log(error)
})
},
When this script is run, the file of files[0] is uploaded to Google Drive with the file metadata of { name: files[0].name, parents: [this.currentFolder.folderId] }.
When a request is run by multipart/form-data with new FormData(), it is not required to set the content type of the request header. The content type including the boundary is automatically created.
In order to set the file metadata, I used formData.append('metadata', new Blob([JSON.stringify({ name: files[0].name, parents: [this.currentFolder.folderId] })], { type: 'application/json' }));.
Note:
In this modification, it supposes that files[0] is the valid file object and your access token can be used for uploading the file to Google Drive using Drive API. Please be careful about this.
References:
Files: create
Upload file data
parents is a list parameter, so you should provide as one in the form field.
Like this:
formData.append("parents[]", this.currentFolder.folderId)
Related
I download my PDF file from Google drive according to the documentation:
const file = await this.driveClient.files.get(
{
fileId: id,
alt: 'media',
},
{
responseType: 'stream'
},
);
Then I construct a form data:
const formData = new FormData();
formData.append('file', file.data, 'file.pdf');
And send it to S3 via the presigned upload url:
const uploadedDocument = await axios({
method: 'put',
url: presignedS3Url,
data: formData,
headers: formData.getHeaders(),
});
The flow works, but the file uploaded to s3 appears corrupted:
I also tried different response types from Google API such as blob. Any idea what I am missing? Thanks in advance!
I managed to solve the issue by piping the file stream from google drive directly to s3 using nodejs built-in packages.
import * as https from 'https';
import { promisify } from 'util';
import { pipeline } from 'stream';
//...
const file = await this.driveClient.files.get(
{
fileId: id,
alt: 'media',
},
{
responseType: 'stream'
},
);
await promisify(pipeline)(
file.data,
https.request(s3SignedUrl, {
method: 'PUT',
headers: {
'content-type': fileMetadata.mimeType,
'content-length': fileMetadata.size,
},
timeout: 60000,
}),
);
You need to export the file as PDF from google drive, use the following function, passing the id:
/**
* Download a Document file in PDF format
* #param{string} fileId file ID
* #return{obj} file status
* */
async function exportPdf(fileId) {
const {GoogleAuth} = require('google-auth-library');
const {google} = require('googleapis');
// Get credentials and build service
// TODO (developer) - Use appropriate auth mechanism for your app
const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});
const service = google.drive({version: 'v3', auth});
try {
const result = await service.files.export({
fileId: fileId,
mimeType: 'application/pdf',
});
console.log(result.status);
return result;
} catch (err) {
console.log(err)
throw err;
}
}
I've been trying to create a file using the Google APIs for Browser. I reused some of the code that I used to communicate with the api from NodeJS in the past and repurposed it for the browser.
const content = "this is some content";
const fileMetadata = {
name: "my-file.txt",
alt: "media",
};
const media = {
mimeType: "text/plain",
body: content,
};
const {
result: { id: fileId },
} = await gapi.client.drive.files.create({
resource: fileMetadata,
media: media,
fields: "id",
});
I typically get a successful response saying that my file was created.
However, when I try to get the contents of the file the body field is an empty string.
const { body } = await gapi.client.drive.files.get({
fileId: fileId,
});
console.log(body)
// ""
I think the request to create the file might not be formatted correctly but it works when it runs on the backend so I'm confused as to why it doesn't work in the browser.
You are mentioning that you are using the Google APIs for browser, and not node.js.
I would recommend to send the request directly against the Google REST API, as gapi.client.drive.create() appears to have problems sending the actual binary file (while sending metadata appears to work). Look here, for example: https://stackoverflow.com/a/53841879/7821823, https://stackoverflow.com/a/35182924/7821823 or https://stackoverflow.com/a/68595887/7821823
You can send the data as a blob and create the request using the FormData class.
async upload(blob, name, mimeType, parents = ["root"]) {
const metadata = { name, mimeType, parents };
const form = new FormData();
form.append("metadata", new Blob([JSON.stringify(metadata)], { type: "application/json" }));
form.append("file", blob);
return fetch("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true", {
method: "POST",
headers: new Headers({ Authorization: `Bearer ${gapi.auth.getToken().access_token}` }),
body: form,
});
}
I have not tested if you can send a String instead of a Blob, but you can easily create a Blob from a String:
const content = "this is some content";
const blob = new Blob([content], { type: 'text/plain' });
The body of the file should be in the form of a stream. Not plain text.
var media = {
mimeType: 'text/plain',
body: fs.createReadStream('files/my-file.txt')
};
as for console.log(body)
Also file.get returns a file.resource File resource does not give you the option to see the contents of the file. Google Drive doesn't have the ability to read the file itself it can only tell you about the file.
I have a method that creates and upload a sheet to Google Drive. When a user clicks on a button that says "create a report", the report is created and uploaded to the user google drive. It works great. However, the response from Google after the sheet is being saved to google drive doesn't contain the sheet id nor the sheet URL. Users are saying that when they click on the "create report" button, they want the google sheet to open in a new tab after it's being saved to their google drive. They don't want to go in their drive to open the file manually. I was thinking that the response being return after the sheet is being uploaded would've contained at least the sheet id or the URL to the resource that's being created in google drive. Does anyone have an idea on how to accomplish what I am driving to do? I am using Google API to upload the file to the users drive.
Here is the response that get sent after the sheet is uploaded
"https://www.googleapis.com/upload/drive/v3/files?
uploadType=multipart&fields=id%2Cname%2Ckind", redirected: false,
status: 200, ok: true, …}
body: ReadableStream
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: ""
type: "cors"
url: "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id%2Cname%2Ckind"
//Here is the code:
let metadata = {
name: excelFileName,
mimeType: "application/vnd.google-apps.spreadsheet",
};
let form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
form.append('file', excelFile);
let url = new URL('https://www.googleapis.com/upload/drive/v3/files');
url.search = new URLSearchParams({ uploadType: 'multipart', fields: 'id,name,kind' });
try {
let res = await fetch(url, {
method: 'POST',
headers: new Headers({ 'Authorization': 'Bearer ' + gapi.auth.getToken().access_token }),
body: form
});
if (res.ok) {
console.log(gapi)
console.log(res)
alert(`"${excelFileName}" has successfully saved to your google drive!`);
} else {
console.error(`Encounter a problem saving excel file, ${excelFileName}, to google drive`);
}
} catch (ex) {
console.error(`Oops excel file for "${excelFileName}" wasn't saved. ${error}`)
}
When you uploaded a file, you want to retrieve the file ID and URLs of the uploaded file.
You want to achieve by modifying your current script.
If my understanding is correct, how about this modification?
Modification points:
In order to return the file ID and URLs, in this case, these fields are used.
exportLinks,id,kind,name,webContentLink,webViewLink
In order to retrieve the returned values from res, it uses res.json().
When above modification points are reflected to your script, it becomes as follows.
Modified script:
Please modify 2 parts of your script as follows.
From:
url.search = new URLSearchParams({ uploadType: 'multipart', fields: 'id,name,kind' });
To:
url.search = new URLSearchParams({ uploadType: 'multipart', fields: 'exportLinks,id,kind,name,webContentLink,webViewLink' });
And
From:
if (res.ok) {
console.log(gapi)
console.log(res)
To:
if (res.ok) {
res.json().then((value) => {
console.log(value)
});
References:
Files
Files: create
If I misunderstood your question and this was not the direction you want, I apologize.
You need to do 2 changes to obtain the URL from the file:
1) You have to add the webViewLink attribute [1] between your fields in the search parameters:
url.search = new URLSearchParams({ uploadType: 'multipart', fields: 'id,name,kind,webViewLink' });
This attribute is the link to open the file in the web.
2) The response body you get is a ReadableStream, you need to convert it to a json object that you can manipulate. This can be done using the json() function [2] to the response you’re getting, which will return the response body parsed to a Json object with the attributes: id, webViewLink, etc.
I tweaked and tested your code and worked as expected, showing the URL for the newly created file in the alert message:
let metadata = {
name: excelFileName,
mimeType: "application/vnd.google-apps.spreadsheet",
};
let form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
form.append('file', excelFile);
let url = new URL('https://www.googleapis.com/upload/drive/v3/files');
url.search = new URLSearchParams({ uploadType: 'multipart', fields: 'id,name,kind,webViewLink' });
try {
const res = await fetch(url, {
method: 'POST',
headers: new Headers({ 'Authorization': 'Bearer ' + gapi.auth.getToken().access_token}),
body: form
});
//Convert response to json
const resJson = await res.json();
if (res.ok) {
console.log(gapi);
console.log(res);
console.log(resJson);
alert(`"${excelFileName}" has successfully saved to your google drive!: ` + resJson.webViewLink);
} else {
console.error(`Encounter a problem saving excel file, ${excelFileName}, to google drive:` + res.Json);
}
} catch (ex) {
console.error(`Oops excel file for "${excelFileName}" wasn't saved. ${ex}`)
}
All the code above inside a sync function of course, to be able to use the await statement.
[1] https://developers.google.com/drive/api/v3/reference/files#resource
[2] https://developer.mozilla.org/en-US/docs/Web/API/Body/json
How to download zip file from reactjs using POST API.
The request is coming from nodejs in binary form
you can use jszip link https://github.com/Stuk/jszip like
import zipTargetFiles from '/path'
zipTargetFiles( data ).then(file => {
//operations
})
if you use fetch like this.
fetch('URL', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
//Body
})
}).then((response)=>{
//here is youu want zip data
var zip = new JSZip();
var zipData = response.data //
// Add an top-level, arbitrary text file with contents
zip.file("response.txt", zipData);
// Generate the zip file asynchronously
zip.generateAsync({type:"blob"})
.then(function(content) {
// Force down of the Zip file
saveAs(content, "zipFile.zip");
});
}).catch((error)=>{
console.log(error)
})
You can use JsZip on Client Side. Then, do a request with axios. Like this:
request = (currentUrl: string): Promise<void> => axios({
url: currentUrl,
method: 'GET',
responseType: 'blob',
}).then((response) => {
const url: string = window.URL.createObjectURL(new Blob([response.data]));
});
I'm trying to upload a file with the Google Drive api, and I have the metadata correct, and I want to ensure that the actual file contents make it there. I have a simple page setup that looks like this:
<div id="upload">
<h6>File Upload Operations</h6>
<input type="file" placeholder='file' name='fileToUpload'>
<button id='uploadFile'>Upload File</button>
</div>
and I have a the javascript setup where the user is prompted to sign in first, and then they can upload a file. Here's the code: (currently only uploads the file metadata....)
let uploadButton = document.getElementById('uploadFile');
uploadButton.onclick = uploadFile;
const uploadFile = () => {
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
console.dir(ftu);
gapi.client.drive.files.create({
'content-type': 'application/json;charset=utf-8',
uploadType: 'multipart',
name: ftu.name,
mimeType: ftu.type,
fields: 'id, name, kind'
}).then(response => {
console.dir(response);
console.log(`File: ${ftu.name} with MimeType of: ${ftu.type}`);
//Need code to upload the file contents......
});
};
First, I'm more familiar with the back end, so getting the file in bits from the <input type='file'> tag is a bit nebulous for me. On the bright side, the metadata is there. How can I get the file contents up to the api?
So According to some resources I've found in my three day search to get this going, the file simply cannot be uploaded via the gapi client. It must be uploaded through a true REST HTTP call. So let's use fetch!
const uploadFile = () => {
//initialize file data from the dom
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
let file = new Blob([ftu]);
//this is to ensure the file is in a format that can be understood by the API
gapi.client.drive.files.create({
'content-type': 'application/json',
uploadType: 'multipart',
name: ftu.name,
mimeType: ftu.type,
fields: 'id, name, kind, size'
}).then(apiResponse => {
fetch(`https://www.googleapis.com/upload/drive/v3/files/${response.result.id}`, {
method: 'PATCH',
headers: new Headers({
'Authorization': `Bearer ${gapi.client.getToken().access_token}`,
'Content-Type': ftu.type
}),
body: file
}).then(res => console.log(res));
}
The Authorization Header is assigned from calling the gapi.client.getToken().access_token function, and basically this takes the empty object from the response on the gapi call and calls the fetch api to upload the actual bits of the file!
In your situation, when you upload a file using gapi.client.drive.files.create(), the empty file which has the uploaded metadata is created. If my understanding is correct, how about this workaround? I have experienced the same situation with you. At that time, I used this workaround.
Modification points:
Retrieve access token using gapi.
File is uploaded using XMLHttpRequest.
Modified script:
Please modify the script in uploadFile().
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
var metadata = {
'name': ftu.name,
'mimeType': ftu.type,
};
var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token.
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
form.append('file', ftu);
var xhr = new XMLHttpRequest();
xhr.open('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,kind');
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xhr.responseType = 'json';
xhr.onload = () => {
console.log(xhr.response);
};
xhr.send(form);
Note:
In this modified script, it supposes that Drive API is enabled at API console and the access token can be used for uploading file.
About fields, you are using id,name,kind. So this sample also uses them.
Reference:
gapi
If I misunderstand your question or this workaround was not useful for your situation, I'm sorry.
Edit:
When you want to use fetch, how about this sample script?
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
var metadata = {
'name': ftu.name,
'mimeType': ftu.type,
};
var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token.
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
form.append('file', ftu);
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,kind', {
method: 'POST',
headers: new Headers({'Authorization': 'Bearer ' + accessToken}),
body: form
}).then((res) => {
return res.json();
}).then(function(val) {
console.log(val);
});
With https://www.npmjs.com/package/#types/gapi.client.drive
const makeUploadUrl = (fileId: string, params: Record<string, boolean>) => {
const uploadUrl = new URL(
`https://www.googleapis.com/upload/drive/v3/files/${fileId}`
)
Object.entries({
...params,
uploadType: 'media',
}).map(([key, value]) => uploadUrl.searchParams.append(key, `${value}`))
return uploadUrl
}
const uploadDriveFile = async ({ file }: { file: File }) => {
const params = {
enforceSingleParent: true,
supportsAllDrives: true,
}
// create file handle
const { result } = await gapi.client.drive.files.create(params, {
// CAN'T have the upload type here!
name: file.name,
mimeType: file.type,
// any resource params you need...
driveId: process.env.DRIVE_ID,
parents: [process.env.FOLDER_ID],
})
// post the file data
await fetch(makeUploadUrl(result.id!, params), {
method: 'PATCH',
headers: new Headers({
Authorization: `Bearer ${gapi.client.getToken().access_token}`,
'Content-Type': file.type,
}),
body: file,
})
return result
})
}