I am trying to integrate Google drive resumable file upload/update with my application. But when i update the file, file is updating in encoded format it is not taking the actual content. Encoded format is working for multiplepart uploadType but same content is not working for Resumable upload. Please find the below details
Step 1 : Start the resumable session
function uploadFile(fileData) {
var accessToken = 'ya29.nwI5Em6UnYGHvVzVx7lBk5tD-xzFl4_JG3_c-_t4FJ3owll_8i_rL5M17LFV6VlF7QE';
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var contentType = fileData.type || 'application/octet-stream';
var metadata = {
'name': fileData.name,
'mimeType': contentType,
'Content-Type': contentType,
'Content-Length': fileData.size
};
var request = gapi.client.request({
'path' : 'upload/drive/v3/files',
'method' : 'POST',
'params' : {'uploadType':'resumable'},
'headers' : {
'X-Upload-Content-Type' : fileData.type,
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + accessToken,
},
'body' : metadata
});
request.execute(function(resp, raw_resp) {
var locationUrl = JSON.parse(raw_resp).gapiRequest.data.headers.location;
console.log(locationUrl);
uploadToLocationUrl(locationUrl, fileData);
});
}
Upto here it's fine I am getting Location Url and then calling a function to upload the file.
Step 2 : Resumable session initiation request
function uploadToLocationUrl(locationUrl, fileData)
{
var reader = new FileReader();
reader.readAsBinaryString(fileData);
reader.onload = function (e) {
var contentType = fileData.type || 'application/octet-stream';
var metadata = {
'name': fileData.name,
'mimeType': contentType,
'Content-Type': contentType,
'Content-Length': fileData.size
};
var base64Data = btoa(reader.result);
var multipartRequestBody =
delimiter +
'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) +
delimiter +
'Content-Type: ' + contentType + '\r\n' +
'Content-Transfer-Encoding: base64\r\n' +
'\r\n' +
base64Data +
close_delim;
var requestPost = gapi.client.request({
'path' : locationUrl,
'method' : 'PUT',
'headers' : {
'X-Upload-Content-Length' : fileData.size
},
'body' : multipartRequestBody
});
console.log(requestPost);
requestPost.execute(function(resp, raw_resp) {
console.log(resp);
});
}
}
Result : Updated file in google drive
---------314159265358979323846
Content-Type: application/json
{"name":"api.txt","mimeType":"text/plain"}
---------314159265358979323846
Content-Type: text/plain
Content-Transfer-Encoding: base64
MSkgTmVlZCBhbiBhcGkgd2hpY2ggd2lsbCByZXR1cm4gYWxsIGxlYWRzIGVtYWlsIGlkLg0KMikgTmVlZCBhbiBhcGkgdG8gY29udmVydCBtdWx0aXBsZSBjb250YWN0IGludG8gbGVhZC4NCjMpIE5lZWQgYW4gYXBpIGZvciBnb29nbGUgc2lnbiBpbi4vLyBkb24ndCBkaXNjdXNzIGFib3V0IHRoaXMgb25lIG5vdywgZmlyc3Qgd2Ugd2lsbCBkaXNjdXNzIGFib3V0IHRoaXMgQVBJLg==
---------314159265358979323846--
Sorry for the delayed answer,
you just need to change the uploadToLocationUrl function code
find below the updated code,
function uploadToLocationUrl(locationUrl, fileData,arrayBuffer)
{
var contentType = fileData.type || 'application/octet-stream';
var requestPost = gapi.client.request({
'path' : locationUrl,
'method' : 'PUT',
'headers' : {
'Content-Type': contentType,
"Content-Length": arrayBuffer.byteLength,
},
'data' : arrayBuffer
});
console.log(requestPost);
requestPost.execute(function(resp, raw_resp) {
console.log(resp);
});
}
you no need to send the metadata again, just byte array is fine.
Related
Using https://developer.safetyculture.io/?shell#add-a-response
I have the following:
function addResponse(){
var url = 'https://api.safetyculture.io/response_sets/responseset_id/responses';
var myBody = {"label": "String To Add"};
var headers = {"Authorization" : 'Bearer ' + 'api_key'};
var params = {
'method': 'POST',
//'muteHttpExceptions': true,
'headers': headers,
'contentType': 'application/json',
'payload': myBody
};
UrlFetchApp.fetch(url, params);
}
I am able to get the responseset (https://developer.safetyculture.io/?shell#get-a-global-response-set) so I know the URL, Responseset_ID and key are correct.
Bad Request","message":"Invalid request payload JSON format"
How should I be correctly formatting myBody?
I want to upload a file to Google Drive but the file is uploaded without a filename. It upload as a 'Untitled' file. Please give me a solution if it works then I accept your answer Anyone here with a solution for this? Thanks in Advance. Here is my code.
userController.uploadToDrive = function(req, res){
token = req.body.token;
console.log(token);
var formData = new FormData();
console.log(token);
var fileMetadata = {
'name': 'all.vcf'
};
formData.append("data",fs.createReadStream('./all.vcf'), "all.vcf");
request({
headers: {
'Authorization': token,
'Content-Type' :'text/x-vcard',
},
resource: fileMetadata,
uri: 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart',
body: formData,
filename:'all.vcf',
method: 'POST'
}, function (err, resp, body) {
if(err){
console.log(err);
}else{
console.log('resp',body);
res.status(200).send()
fs.readdir('./contacts', function (err, files) {
var removefiles = function (file) {
fs.unlinkSync('./contacts/' + file)
}
files.forEach(function (file) {
removefiles(file)
})
})
}
});
}
It response like this:
resp {
"kind": "drive#file",
"id": "1tXu9Fc4sdi-yk8QGGvMJqSgxLXhuXNhQ",
"name": "Untitled",
"mimeType": "text/x-vcard"
}
I believe your goal and situation as follows.
You want to upload a file to Google Drive using multipart/form-data with Drive API.
Your access token can be used for uploading the file to Google Drive.
I think that in your case, the metadata and file content cannot be uploaded as multipart/form-data. By this, the file metadata cannot be reflected to the uploaded file. So in order to achieve this, I would like to propose the following modification.
Pattern 1:
In this pattern, const request = require("request") is used.
Modified script:
const fs = require("fs");
const request = require("request");
token = req.body.token;
fs.readFile("./all.vcf", function (err, content) {
if (err) {
console.error(err);
}
const metadata = {
name: "all.vcf",
mimeType: "text/x-vcard"
};
const boundary = "xxxxxxxxxx";
let data = "--" + boundary + "\r\n";
data += 'Content-Disposition: form-data; name="metadata"\r\n';
data += "Content-Type: application/json; charset=UTF-8\r\n\r\n";
data += JSON.stringify(metadata) + "\r\n";
data += "--" + boundary + "\r\n";
data += 'Content-Disposition: form-data; name="file"\r\n\r\n';
const payload = Buffer.concat([
Buffer.from(data, "utf8"),
Buffer.from(content, "binary"),
Buffer.from("\r\n--" + boundary + "--\r\n", "utf8"),
]);
request(
{
method: "POST",
url:
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart",
headers: {
Authorization: token,
"Content-Type": "multipart/form-data; boundary=" + boundary,
},
body: payload,
},
function (err, resp, body) {
if(err){
console.log(err);
}else{
console.log('resp',body);
res.status(200).send()
fs.readdir('./contacts', function (err, files) {
var removefiles = function (file) {
fs.unlinkSync('./contacts/' + file)
}
files.forEach(function (file) {
removefiles(file)
})
})
}
}
);
});
Pattern 2:
In this pattern, node fetch is used. In your script, new FormData() is used. So I thought that this pattern might be the direction you expect.
Modified script:
const FormData = require("form-data");
const fetch = require("node-fetch");
const fs = require("fs");
token = req.body.token;
var formData = new FormData();
var fileMetadata = {
name: "all.vcf",
mimeType: "text/x-vcard",
};
formData.append("metadata", JSON.stringify(fileMetadata), {
contentType: "application/json",
});
formData.append("data", fs.createReadStream("./all.vcf"), {
filename: "all.vcf",
});
fetch(
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart",
{ method: "POST", body: formData, headers: { Authorization: token } }
)
.then((res) => res.json())
.then((json) => console.log(json));
Note:
In the case of uploadType=multipart, the maximum file size is 5 MB. Please be careful this. When you want to upload more large size, please use the resumable upload. Ref
References:
Upload file data
node-fetch
I am running into a problem with google drive rest api. I have a button and upon the user click, I get a blob excel file from my backend and upload the file to google drive. The file is being uploaded to the google drive, but when I opened it, it says '[object blob]'. The actual content isn't in the file. Here is my function for creating the file. I found this solution from here: Create File with Google Drive Api v3 (javascript)
var UploadExcelFile = function(name, data, callback){
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
const contentType = "application/vnd.google-apps.spreadsheet";
var metadata = {
'name': name,
'mimeType': contentType
};
var multipartRequestBody =
delimiter +
'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) +
delimiter +
'Content-Type: ' + contentType + '\r\n\r\n' +
data +
close_delim;
var request = gapi.client.request({
'path': '/upload/drive/v3/files',
'method': 'POST',
'params': {'uploadType': 'multipart'},
'headers': {
'Content-Type': 'multipart/related; boundary="' + boundary + '"'
},
'body': multipartRequestBody});
if (!callback) {
callback = function(file) {
console.log(file)
};
}
request.execute(callback);
}```
```This is the response from the server:
Response {type: "basic", url:
"http://localhost:54878/home/generateexcel", redirected: false,
status:
200, ok: true, …}
body: ReadableStream
locked: true
__proto__: ReadableStream
bodyUsed: true
headers: Headers
__proto__: Headers
ok: true
redirected: false
status: 200
statusText: "OK"
type: "basic"
url: "http://localhost:54878/home/generateexcel"}
I am passing in the name of the file and the blob excel file from the backend like so:
fetch('/home/generateexcel', {
method: 'POST',
body: JSON.stringify(postData),
headers: {
"Content-Type": "application/json"
},
}).then(function (response) {
response.blob().then(function (result)
UploadExcelFile('newfile', result)
});
}).catch(function (err) {
// Error :(
});
You want to upload the downloaded xlsx file to Google Drive.
You have already confirmed that the xlsx file could be downloaded.
When a xlsx file is uploaded, you want to convert to Google Spreadsheet.
You can use Drive API and the access token for uploading files.
If my understanding is correct, how about this modification? In this modification, I used FormData() for creating the request body and used fetch() for requesting to Drive API. I think that there are several solutions for your situation. So please think of this as just one of them.
Modified script:
I modified UploadExcelFile(). Please modify as follows and try again.
function UploadExcelFile(name, data) {
var metadata = {
name: name,
mimeType: "application/vnd.google-apps.spreadsheet",
};
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
form.append('file', data);
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,kind', {
method: 'POST',
headers: new Headers({'Authorization': 'Bearer ' + gapi.auth.getToken().access_token}),
body: form
}).then((res) => {
return res.json();
}).then(function(val) {
console.log(val);
});
}
In my environment, I could confirm that this script worked. But if this didn't work in your environment, I apologize.
I'm trying to send a post request from my node server to an api that expects a file and other form data as an multipart/form-data.
Here is what my code looks like
var importResponse = function(csv){
stringify(csv, function(err, output){
request.post({
headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
url: url,
formData: {
surveyId: surveyId,
file: {
value: output,
options: {
fileName: 'test.csv',
contentType:'text/csv'
}
}
}
}, function(error, response, body){
console.log(body);
});
});
}
Using request-debug here is the request:
request:
{ debugId: 1,
uri: 'https://co1.qualtrics.com/API/v3/responseimports',
method: 'POST',
headers:
{ 'X-API-TOKEN': 'removed',
'content-type':
'multipart/form-data; boundary=--------------------------010815605562947295265820',
host: 'co1.qualtrics.com',
'content-length': 575 } } }
and the response:
response:
{ debugId: 1,
headers:
{ 'content-type': 'application/json',
'content-length': '188',
'x-edgeconnect-midmile-rtt': '28',
'x-edgeconnect-origin-mex-latency': '56',
date: 'Wed, 18 Jul 2018 03:57:59 GMT',
connection: 'close',
'set-cookie': [Array],
'strict-transport-security': 'max-age=31536000; includeSubDomains; preload' },
statusCode: 400,
body:
'{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Missing Content-Type for file part. name=file","errorCode":"MFDP_3"},"requestId":"322a16db-97f4-49e5-bf10-2ecd7665972e"}}' } }
The error I'm getting is: Missing Content-Type for file part.
I've added this in the options:
options: {
fileName: 'test.csv',
contentType:'text/csv'
}
When I look at the request, it seems as though the form data isn't included. But perhaps that is just the request-debug not showing it.
I saw a similar SO question and the answer was to use JSON.stringify.
I tried changing my code to the following:
request.post({
headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
url: url,
body: JSON.stringify({
surveyId: surveyId,
file: {
value: output,
options: {
fileName: 'test.csv',
contentType:'text/csv'
}
}
})
However, I got the following error:
{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Missing boundary header"}}}
What am I doing wrong?
UPDATE
When I tried changing the file value to a csv on my computer fs.createReadStream('test.csv'), it worked fine
file: {
value: fs.createReadStream('test.csv'),
options: {
contentType: 'text/csv'
}
}
So I assume there is something wrong with the way I'm giving the file. The output variable that I'm using as the file just looks like "QID1,QID2\nQID1,QID2\n1,2". I assume this is causing the problems, even though the error is a bit misleading. I tried creating a Readable that I found as a StackOverFlow answer like so:
var s = new Readable
s.push(output)
s.push(null)
However, this lead to a Unexpected end of input
{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Unexpected end of input"}}}
I found the issue. My first solution was fine, but instead of fileName it should have been filename
var importResponse = function(csv){
stringify(csv, function(err, output){
request.post({
headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
url: url,
formData: {
surveyId: surveyId,
file: {
value: output,
options: {
filename: 'test.csv', //filename NOT fileName
contentType:'text/csv'
}
}
}
}, function(error, response, body){
console.log(body);
});
});
}
Is it possible that you are using the incorrect property name for your file?
A quick read of the forms info for the request Node module makes me think you should be using custom_file instead of file.
You can read more about it here: https://github.com/request/request#forms
Hey Eric check in which format they accept multipartRequest ,as I did for uploading the file on drive like this:
request(options, function (err, response) {
var boundary = '-------314159265358979323846';
var delimiter = "\r\n--" + boundary + "\r\n";
var close_delim = "\r\n--" + boundary + "--";
var fileContent = 'Sample upload :)';
var metadata = {
'name': 'myFile.txt',
'mimeType': 'text/plain\r\n\r\n'
};
var multipartRequestBody = delimiter + 'Content-Type: application/json\r\n\r\n' + JSON.stringify(metadata) + delimiter + 'Content-Type: ' + 'text/plain\r\n\r\n' + fileContent + close_delim;
request(options, function (err, response) {
var url = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&access_token=' + JSON.parse(response.body).access_token;
var options = {
method: 'POST',
url: url,
headers: {
'Content-Type': 'multipart/related; boundary="' + boundary + '"'
},
body: multipartRequestBody
};
request(options, function (err, response) {
res.send({resultdata: response.body});
});
});
});
Set the multi-part as per your endpoint accepting.
Using frameworks like express(they automatically parse headers and response) and npm modules like multer for handling multipart form data helps cause they do all the heavy lifting for you
Hi I am developing a web application with GDrive api in javascript.
In my page I can upload a file to the GDrive folder of the logged person.
What I want is that the file is public access to other user with a link to download the file.
var reader = new FileReader();
reader.readAsBinaryString(fileData);
reader.onload = function(e) {
var contentType = fileData.type || 'application/octet-stream';
var metadata = {
'title': fileData.name,
'mimeType': contentType
};
var body = {
'value': 'google account',
'type': 'anyone',
'role': 'reader'
};
var base64Data = btoa(reader.result);
var multipartRequestBody =
delimiter +
'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) +
delimiter +
'Content-Type: ' + contentType + '\r\n' +
'Content-Transfer-Encoding: base64\r\n' +
'\r\n' +
base64Data +
close_delim;
var request = gapi.client.request({
'path': '/upload/drive/v2/files',
'method': 'POST',
'params': {'uploadType': 'multipart'},
'headers': {
'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
},
'body': multipartRequestBody});
if (!callback) {
callback = function(file) {
console.log(file)
};
}
request.execute(callback);
};