Based on #Nvico great answer i was able to upload files to google drive, the problem is the code on the answer creates a new file each time, is there a way given an already created file id to update its content directly (using Files:update api) without creation of a new one ?
currently my solution is to use Files:delete api each time i want to update the file to remove the old one then create a new file using #Nvico code
Instead of using the drive.files.insert endpoint, you can use almost the same code to send an update request to the drive.files.update endpoint:
/**
* Update existing file.
*
* #param {String} fileId ID of the file to update.
* #param {Object} fileMetadata existing Drive file's metadata.
* #param {File} fileData File object to read data from.
* #param {Function} callback Callback function to call when the request is complete.
*/
function updateFile(fileId, fileMetadata, fileData, callback) {
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var reader = new FileReader();
reader.readAsBinaryString(fileData);
reader.onload = function(e) {
var contentType = fileData.type || 'application/octect-stream';
// Updating the metadata is optional and you can instead use the value from drive.files.get.
var base64Data = btoa(reader.result);
var multipartRequestBody =
delimiter +
'Content-Type: application/json\r\n\r\n' +
JSON.stringify(fileMetadata) +
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/' + fileId,
'method': 'PUT',
'params': {'uploadType': 'multipart', 'alt': 'json'},
'headers': {
'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
},
'body': multipartRequestBody});
if (!callback) {
callback = function(file) {
console.log(file)
};
}
request.execute(callback);
}
}
The main difference is in the request URL and method:
PUT /upload/drive/v2/files/<FILE_ID>
Related
I am quite unexperienced with file compression and google drive api. In my project I am using zip.js and Google Drive Api. The zip.js functions well because I download directly the generated zip file and my device can decompress it. Eventhough I don't add any extra data to the content of the zip, the zip file size is changed (doubled), and is corrupted when it is updated.
My function to generate a zip file:
function createZip(data,fileName,callback){
var blob = new Blob([ data ], {
type : "text/xml"
});
zipBlob(fileName, blob, function(zippedBlob){
console.log(zippedBlob)
// saveAs(zippedBlob,"test.zip") //this test.zip is a valid file.
var reader = new FileReader();
reader.addEventListener("loadend", function() {
// reader.result contains the contents of blob as a typed array
console.log(reader.result);
callback(reader.result);
});
reader.readAsText(zippedBlob)
})
function zipBlob(filename, blob, callback) {
zip.createWriter(new zip.BlobWriter("application/zip"), function(zipWriter) {
zipWriter.add(filename, new zip.BlobReader(blob), function() {
zipWriter.close(callback);
});
}, function(error){
console.error(error)
});
}
}
My function to update the file:
this.updateFile = function(id, text, accessToken, callback) {
var boundary = '-------314159265358979323846',
delimiter = "\r\n--" + boundary + "\r\n",
close_delim = "\r\n--" + boundary + "--",
mimeType = 'application/zip';
var multipartRequestBody =
delimiter + 'Content-Type: application/json\r\n\r\n' +
delimiter + 'Content-Type:' + mimeType + '\r\n\r\n' +
text +
close_delim;
gapi.client.request({
'path': '/upload/drive/v3/files/'+id,
'method': 'PATCH',
'params': { 'uploadType': 'multipart' },
'headers': { 'Content-Type': 'multipart/mixed; boundary="' + boundary + '"', 'Authorization': 'Bearer ' + accessToken, },
'body': multipartRequestBody
}).execute(function(file) {
if (typeof callback === "function") callback(file);
}, function(error) {
console.error(error)
callback(error);
});
}
I wonder if the FileReader corrupts the content because the reader.result is different than the original content of the zip. I use this function to read the file content:
this.readFile = function(fileId, callback) {
var request = gapi.client.drive.files.get({
fileId: fileId,
alt: 'media',
Range : 'bytes=100-200'
})
request.then(function(response) {
console.log(response);
if (typeof callback === "function") callback(response.body);
}, function(error) {
console.error(error)
})
//return request;
}
The original content of the zip as text:
PKÊL PV-A2.xmlUXñçûZÂÌZùì½m,I÷]þC£?I#ÌNøK¼=F °KÃ(«Ýgz=Crçß+²ne§y¸¥U6... (some thousands characters more)
File reader content as text:
PK�U�L PV-A2.xml�]�<��w�O1�+(���� �#�6`#+ �Ґ`��r��EÑ�������̊s"���0�..(some thousands characters more)
SOLUTION
Get base64 data from the zip file (remember to strip data:application/zip;base64, out):
// saveAs(zippedBlob,"test.zip") //this test.zip is a valid file.
var reader = new FileReader();
reader.addEventListener("loadend", function() {
// reader.result contains the contents of blob as a typed array
callback(reader.result.replace("data:application/zip;base64,",""));
});
reader.readAsDataURL(zippedBlob)
Add Content-Transfer-Encoding: base64 to multipartRequestBody and use the base64 content instead.
var multipartRequestBody =
delimiter + 'Content-Type: application/json\r\n\r\n' +
delimiter + 'Content-Type:' + mimeType + '\r\n' +
'Content-Transfer-Encoding: base64\r\n\r\n' +
base64Data +
close_delim;
If text is the zip file which was converted to base64, how about this modification? I think that your script is almost correct. As a modification, it gives the encode method of the file using Content-Transfer-Encoding.
From :
delimiter + 'Content-Type:' + mimeType + '\r\n\r\n' +
text +
To :
delimiter + 'Content-Type: ' + mimeType + '\r\n' + // Modified
'Content-Transfer-Encoding: base64\r\n\r\n' + // Added
text +
Note :
In my environment, I confirmed that the same error with your situation occurs using your script. And also I confirmed that when this modification is reflected to your script, the zip file is updated, and the file can be unzipped.
But if this was not the solution of your situation, I'm sorry.
I am getting 404 error on my $.ajax request in Google API.
I have these codes,
var asyncLoad = require('react-async-loader');
var CLIENT_ID = '<SOME_ID>';
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"];
var SCOPES = 'https://www.googleapis.com/auth/drive';
const mapScriptToProps = state => ({
gapi: {
globalPath: 'gapi',
url: 'https://apis.google.com/js/api.js'
}
});
#asyncLoad(mapScriptToProps)
...
I used async loader of react to get the Google API before the bundle.js.
Then I get the gapi in the properties. Here is my next codes for submitting a form.
submitForm(e){
e.preventDefault();
var data = this.refs.file.files[0];
var self = this;
var formData = new FormData();
formData.append('data', data);
$.ajax({
url: "https://www.googleapis.com/upload/drive/v3?uploadType=media&access_token="+encodeURIComponent(self.state.token),
type: "POST",
processData: false,
data: formData,
beforeSend: function (xhr) {
/* Authorization header */
xhr.setRequestHeader("Authorization", "Bearer " + self.state.token);
xhr.setRequestHeader('X-Upload-Content-Length', data.size);
xhr.setRequestHeader("Content-Type", "image/jpeg");
xhr.setRequestHeader('X-Upload-Content-Type', "image/jpeg");
},
success: function(data){
if(typeof data === "string") data = JSON.parse(data);
console.log(data);
if(data.success){
console.log("done");
}else {
console.log("error");
}
}
});
}
So here, I call submitForm function when the button upload is clicked. I have also file input with ref="file". This is run in client (browser) side. I got 404 error.
What I am trying to do here is to upload an image file to google drive. How can I do this right? Any solution for my problem?
The 404 error is caused because the url is missing the files part: https://www.googleapis.com/upload/drive/v3/files?uploadType=media. See Google Drive APIs > REST - Files: create.
I managed to upload an image that was read from <input type="file" accept="image/*"> with FileReader.readAsDataURL() method as follows:
var metadata = {
name: 'image.jpg',
mimeType: 'image/jpeg'
}
var user = gapi.auth2.getAuthInstance().currentUser.get();
var oauthToken = user.getAuthResponse(true).access_token;
var boundary = 'foo_bar_baz';
var data = '--' + boundary + '\n';
data += 'content-type: application/json; charset=UTF-8' + '\n\n';
data += JSON.stringify(metadata) + '\n';
data += '--' + boundary + '\n';
var dataURL = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAZAAA/+EKhWh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZ...'
var dataURLparts = dataURL.split(',', 2);
var dataURLheaderParts = dataURLparts[0].split(':');
var dataURLheaderPayloadParts = dataURLheaderParts[1].split(';');
data += 'content-transfer-encoding: ' + dataURLheaderPayloadParts[1] + '\n';
data += 'content-type: ' + dataURLheaderPayloadParts[0] + '\n\n';
data += dataURLparts[1] + '\n';
data += '--' + boundary + '--';
$.ajax({
type: 'POST',
url: 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart',
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + oauthToken);
},
contentType: 'multipart/related; boundary=' + boundary,
data: data,
processData: false
}).done(function(response) {
console.log(response);
});
After many hours of hair pulling and viewing the same answers. The only thing that worked for me was to change from the multipart request (that Google documents) to using FormData.
Credit to this answer.
While the data arrived to Drive (successful upload), it wasnt processed correctly so image or PDF were not viewable and downloading it showed it was saved in base64.
const metadata = JSON.stringify({
name: myFile.name,
mimeType: myFile.type,
});
const requestData = new FormData();
requestData.append("metadata", new Blob([metadata], {
type: "application/json"
}));
requestData.append("file", items[0].file);
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart");
const token = gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse().access_token;
xhr.setRequestHeader("Authorization", `Bearer ${token}`);
xhr.send(requestData);
I have been trying to upload a simple text file that holds a user's email and name to a folder that is in the root of my drive. I create the folder and then upload the file to that folder using its ID.
Everything works fine except for the config file not being added to the new folder. It just sits in the root along with the new folder.
I have looked over many of the questions related to this and have tried solutions from almost all of them. This question in particular (Insert a file to a particular folder using google-drive-api) seems like my exact problem. But as you can see, I implemented his change, and I still have the same problem.
Below is the function that is called to do this.
/**
* Creates the correct directory structure and a config file in the user's drive;
*/
function setupDrive(email, name) {
// TODO create CSR folder and config file inside
createFolder('CSR');
uploadConfig(email, name);
checkAuth();
}
This is the createFolder() function.
/**
* Creates a folder with the given name in the drive;
*/
function createFolder(dirName) {
var metadata = {
'name' : dirName,
'mimeType' : 'application/vnd.google-apps.folder'
};
var request = gapi.client.request({
'path': '/drive/v3/files',
'method': 'POST',
'body': JSON.stringify(metadata)});
request.execute();
}
This is the uploadConfig() function.
/**
* Uploads a config file to the CSR folder with the given email and name;
*/
function uploadConfig(email, name) {
var request = gapi.client.request({
'path': '/drive/v3/files',
'method': 'GET',
'q': 'name="CSR", trashed="false", mimeType="application/vnd.google-apps.folder"',
'fields': "nextPageToken, files(id, name)"
});
request.execute(function (results) {
var files = results.files;
var csrID = '';
if (files && files.length > 0) {
csrID = files[0].id;
}
uploadFile('config', email + '\n' + name, 'plain', csrID);
});
}
And finally, the uploadFile() function.
/**
* Uploads either a plain text file or a CSV file to the user's Google Drive in the CSR folder;
*/
function uploadFile(fileName, fileContent, mimeType, parentID) {
console.log(parentID); //check that a parentID is being passed in
var auth_token = gapi.auth.getToken().access_token;
var metaType = '';
var bodyType = '';
if (mimeType == 'csv') {
metaType = 'application/vnd.google-apps.spreadsheet';
bodyType = 'text/csv\r\n\r\n';
} else if (mimeType == 'plain') {
metaType = 'text/plain\r\n\r\n';
bodyType = 'text/plain\r\n\r\n';
}
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var metadata = {
'name': fileName,
'mimeType': metaType,
'parents':[{'id': parentID}]
};
var multipartRequestBody =
delimiter + 'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) +
delimiter + 'Content-Type: ' + bodyType +
fileContent +
close_delim;
var request = gapi.client.request({
'path': '/upload/drive/v3/files',
'method': 'POST',
'params': {'uploadType': 'multipart'},
'headers': { 'Content-Type': 'multipart/form-data; boundary="' + boundary + '"', 'Authorization': 'Bearer ' + auth_token, },
'body': multipartRequestBody
})
request.execute(function (file) {
console.log("Wrote to file " + file.name + " id: " + file.id);
});
}
Any help is greatly appreciated, and I apologize for so much code!
EDIT:
I was able to get it to work when I did everything through REST V2. However, I'd still be interested in seeing a solution that would allow me to use V3.
I don't see any error with your code as compared with this related SO post. It stated that v2 process is very similar with v3. You already changed the version in the url to v3 and the parameter according to the v3. Also stated in this thread, be noted that in v3, there is no longer a parents collection. Instead, you get the parents property by doing a files.get with the child's ID. Ideally, you would use the fields parameter to restrict the response to just the parent(s). This SO answer might also help wherein the OP only used parents: ['parentID'] in the metadata.
This works fine with V3
function createFile(){
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var fileContent = 'It works :)';
var metadata = {
'name': 'myFile',
'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;
gapi.client.request({
'path': '/upload/drive/v3/files',
'method': 'POST',
'params': {
'uploadType': 'multipart'
},
'headers': {
'Content-Type': 'multipart/related; boundary="' + boundary + '"'
},
'body': multipartRequestBody
}).then(function(response){
console.log(response);
});
}
I would like to allow users of my site to be able to upload files that they have created that are stored on my server to be uploaded to there google drive account.
I tried authenticated and passing this accesstoken to .net but couldnt get that flow to work.
Using existing access token for google drive request in .net
So now i need helping in doing this with just javascript. How can i download the file in the background and then pass it to the api?
I would like to avoid using the Save to Drive button if possible.
Here is my current code:
gapi.client.load('drive', 'v2', function() {
//How do i download a file and then pass it on.
var file =
insertFile(file);
});
/**
* Insert new file.
*
* #param {File} fileData File object to read data from.
* #param {Function} callback Function to call when the request is complete.
*/
function insertFile(fileData, callback) {
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
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 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);
}
}
There are many examples of downloading a file from GET request to binary string using XMLHttpRequest. For example, like this one. You can replace the part that reads file as a binary string reader.readAsBinaryString(fileData); to this code.
I have a site where the user can design smartphone cases. At one point the user should be able to share the design on Facebook, including the design. I have the object as well as a , with the 'style' set as the data URI of the canvas.
The code for custom image while sharing is:
How would I go to share it with my image, as it's a data URI.
Thanks
UPDATE:
I now have the canvas saved down on the server, linked correctly. Although, I can't seem to be able to edit the "href" of the link Facebook read the thumbnail picture from.
I tried:
var fblink = document.getElementById("facebook_share");
fblink.href="http://example.com/image.png";
AND
fblink.setAttribute("href", "http://example.com/image.png");
None seem to work. The 'fblink' object is correct as I can read the 'rel' etc.
I personally used canvas.toDataURL() which generates a base64 encoded URL of your canvas content.
After that I decoded the URL using the following command Base64Binary.decode(encodedPng)
Once you have your decoded image you can put it in a form and send all that through an XMLHttpRequest object as specified in the code below:
// Random boundary defined to separate element in form
var boundary = '----ThisIsTheBoundary1234567890';
// this is the multipart/form-data boundary we'll use
var formData = '--' + boundary + '\r\n';
formData += 'Content-Disposition: form-data; name="source"; filename="' + filename + '"\r\n';
formData += 'Content-Type: ' + mimeType + '\r\n\r\n';
// let's encode our image file
for ( var i = 0; i < imageData.length; ++i ) {
formData += String.fromCharCode( imageData[ i ] & 0xff );
}
formData += '\r\n';
formData += '--' + boundary + '\r\n';
formData += 'Content-Disposition: form-data; name="message"\r\nContent-Type: text/html; charset=utf-8\r\n\r\n';
formData += message + '\r\n'
formData += '--' + boundary + '--\r\n';
// Create a POST XML Http Request
var xhr = new XMLHttpRequest();
xhr.open( 'POST', 'https://graph.facebook.com/me/photos?access_token=' + authToken, true );
// Call back function if POST request succeed or fail
xhr.onload = xhr.onerror = function() {
if ( !(xhr.responseText.split('"')[1] == "error") ) {
// If there is no error we redirect the user to the FB post she/he just created
var userID = xhr.responseText.split('"')[7].split('_')[0];
var postID = xhr.responseText.split('"')[7].split('_')[1];
w = window.open('https://www.facebook.com/'+userID+'/posts/'+postID,
'width=1235,height=530');
}
else {
alert("Erreur: "+xhr.responseText);
}
};
xhr.setRequestHeader( "Content-Type", "multipart/form-data; boundary=" + boundary );
// Attach the data to the request as binary
xhr.sendAsBinary( formData );
You can see a full working example on my Github project in the file maskToFb.html