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);
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 trying to send file with XMLHttpRequest with this code.
var url= "http://localhost:80/....";
$(document).ready(function(){
document.getElementById('upload').addEventListener('change', function(e) {
var file = this.files[0];
var xhr = new XMLHttpRequest();
xhr.file = file; // not necessary if you create scopes like this
xhr.addEventListener('progress', function(e) {
var done = e.position || e.loaded, total = e.totalSize || e.total;
console.log('xhr progress: ' + (Math.floor(done/total*1000)/10) + '%');
}, false);
if ( xhr.upload ) {
xhr.upload.onprogress = function(e) {
var done = e.position || e.loaded, total = e.totalSize || e.total;
console.log('xhr.upload progress: ' + done + ' / ' + total + ' = ' + (Math.floor(done/total*1000)/10) + '%');
};
}
xhr.onreadystatechange = function(e) {
if ( 4 == this.readyState ) {
console.log(['xhr upload complete', e]);
}
};
xhr.open('post', url, true);
xhr.setRequestHeader("Content-Type","multipart/form-data");
xhr.send(file);
}, false);
});
I get this error: T
The request was rejected because no multipart boundary was found.
What am I doing wrong?
There is no such thing as xhr.file = file;; the file object is not supposed to be attached this way.
xhr.send(file) doesn't send the file. You have to use the FormData object to wrap the file into a multipart/form-data post data object:
var formData = new FormData();
formData.append("thefile", file);
xhr.send(formData);
After that, the file can be access in $_FILES['thefile'] (if you are using PHP).
Remember, MDC and Mozilla Hack demos are your best friends.
EDIT: The (2) above was incorrect. It does send the file, but it would send it as raw post data. That means you would have to parse it yourself on the server (and it's often not possible, depend on server configuration). Read how to get raw post data in PHP here.
A more extended answer:
let formData = new FormData();
formData.append("fileName", audioBlob);
$.ajax({
"url": "https://myserver.com/upload",
"method": "POST",
"headers": {
"API-Key": "myApiKey",
},
"async": true,
"crossDomain": true,
"processData": false,
"contentType": false,
"mimeType": "multipart/form-data",
"data": formData
}).done(function(response){
console.log(response);
});
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'm trying to download and display an image that is returned from a wcf service using jQuery.ajax. I'm not able to work with the data I've received and I'm not sure why. I've tried a lot of things but nothing seems to work.
Here the service :
public Stream DownloadFile(string fileName, string pseudoFileName)
{
string filePath = Path.Combine(PictureFolderPath, fileName);
if (System.IO.File.Exists(filePath))
{
FileStream resultStream = System.IO.File.OpenRead(filePath);
WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-www-form-urlencoded";
return resultStream;
}
else
{
throw new WebFaultException(HttpStatusCode.NotFound);
}
}
Here my ajax call :
$.ajax({
type: "GET",
url: apiURL + serviceDownloadFile.replace('{filename}', filename),
headers: headers,
contentType: "application/x-www-form-urlencoded",
processData : false,
success: function(response) {
var html = '<img src="data:image/jpeg;base64,' + base64encode(response) +'">';
$("#activitiesContainer").html(html);
},
error: function (msg) {
console.log("error");
console.log(msg);
}
});
Putting the url in a <img> tag does display the image properly, but since the service requires an authorization header, the page ask me for credentials each time.
So my question is, what to do this the response data so I can display it ? using btoa(); on the response displays an error :
string contains an invalid character
Thanks.
As suggested by Musa, using XMLHttpRequest directly did the trick.
var xhr = new XMLHttpRequest();
xhr.open('GET', apiURL + serviceDownloadFile.replace('{filename}', filename).replace('{pseudofilename}', fileNameExt), true);
xhr.responseType = 'blob';
xhr.setRequestHeader("authorization","xxxxx");
xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.response;
var img = document.createElement('img');
img.onload = function(e) {
window.URL.revokeObjectURL(img.src); // Clean up after yourself.
};
img.src = window.URL.createObjectURL(blob);
document.body.appendChild(img);
}
};
xhr.send();
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.