Upload a file to Google Drive API using HTML5 - html

I'm creating a Google Chrome extension which use Google Drive API.
I have to upload a file with HTML5.
For text files, there is no problem. But when I want to upload a binary file, there are always errors.
So when I upload a file using the FileReader in HTML5 as BinaryString, my image is corrupted, I can't read it.
And when I use Base64 encoding (with the header in the body part "Content-Transfer-Encoding: base64"), I have a 400 Bad Request -> Malformed multipart body.
Can you help me please ?
Thanks :)
PS: I don't want to use Google Drive SDK, I prefer write all the code.
var bb, reader;
var meta = {
"title": "mozilla.png",
"mimeType": "image/png",
"description": "Mozilla Official logo"
};
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://developer.mozilla.org/media/img/mdn-logo-sm.png', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e){
if(this.status == 200){
bb = new WebKitBlobBuilder();
bb.append(this.response);
console.log('Download OK');
reader = new FileReader();
reader.readAsDataURL(bb.getBlob('image/png'));
reader.onloadend = function(e){
console.log('Reader OK');
var bound = 287032396531387;
var parts = [];
parts.push('--' + bound);
parts.push('Content-Type: application/json');
parts.push('');
parts.push(JSON.stringify(meta));
parts.push('--' + bound);
parts.push('Content-Type: image/png');
parts.push('Content-Transfer-Encoding: base64');
parts.push('');
parts.push(reader.result);
parts.push('--' + bound + '--');
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart", true);
xhr.setRequestHeader("Authorization", "Bearer token123456");
xhr.setRequestHeader("Content-Type", "multipart/mixed; boundary=" + bound);
xhr.onload = function(e){
console.log("DRIVE OK", this, e);
};
xhr.send(parts.join("\r\n"));
}
}
};
xhr.send();
For Binary Upload, just modify this line :
reader.readAsDataURL(bb.getBlob('image/png'));
by that
reader.readAsBinaryString(bb.getBlob('image/png'));
and delete this line :
parts.push('Content-Transfer-Encoding: base64');
I tried to create a file by sending the metadata first and upload the content after like in this post and I always get a 404 error for uploading the content, but this is another story...

An empty line which consists of only \r\n and no other whitespace need to be added at the end of your request. Try to add another parts.push(''); after parts.push('--' + bound + '--');
Edit:
First, I want to say that you should not upload file as raw Binary String because your binary data contains control characters which may screw up your request and results in corrupted file. Data should be encoded in Base64. You can read more here
If you check reader.result in debug, it will contain:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4AAABHCAMAAABoMgR/AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJlQTFRFAAAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAAwAAEgICExAQIAQFIyAgLwYHMjAwPggJQkBATAoLUlBQWwwOYmBgaQ4QcXBweBASgYCAhxIUkIKCkY+PlRQXoZ+fpBYZrYaGsK+vsxgbwL+/wRod0Bwg0M/P3h4i4N/f7SAk7+/v9pCS////7yG8VgAAAA90Uk5TABAgMEBQYHCAn6+/z9/vBoviEQAABBtJREFUWMOtmA13mzoMhumWdd3uWpuPACUEWIkXA3HI+P8/7kq2AYOBNj1Tz2nA9oOl10KAHUfZw+7p4/b41ZnY7j9yn708PQzww09yv7186+lf5FP2qHBJJ1y0H7am9JCR8z/BAa3bOy1B/zH+lzW6KJt3+CfHeYSfYqGboX9eylRMC5FB/7Pj/IBRdicPoTkOMMIgZWlAEmtIBn1fHFjx0JIGPQt40wiWSZGoZ/Mc2ncLuMgoABAQpXCWEi9hraAWv4KXFP1G1dIYBVSy1ha/iHOM12PzmdLU4hfwBhUj6VRKnqA/GfBxvYWLVCbjMDVLwrBoUOM9/JXAw0rWazgqhp63LMTIa4+mjBdeCiMl2IoyNq4wxWWaoGUwGzjASKxiKALwq6gHj+AK5RyXaaIMZoaxgga9AmlipWu5LJ1ISKwZNYVsnEmeeR66Z+O45KzPaBF7ujkkibkWRSiC0MbVkusxhLShbOesxcCCjI+J1WYWLhItXI9DAy5ASNtel3C4hIUXtBeOyduXkpKTgPGYZI3qqU6R319ihgtIELCABBi1ktcTMvvTNtZ4lXent1fpRRIuSVfj7Jzrw6ARnDcYlEyJox9dSHeqLsdXlyzimVF4CqyfWZYCmkBtxHzz96RyT+djl5PUxmXexaO+ZEhDfdfJ4nNwo4qQeo43Wt/WiERaahZodMJ1UaIJLrJ+LmZ6rxesqM1bn6oQTbzm0goyJii4ystErydN+qqNpSwUK9WGDmkndAbWKuS+akN+6Fpk4zKEelCud4SNzzVQNxMr1UY9ubTQsSEDRFCXiewN16pNraUPWsFl0tP5Hd6USbla61A6Jp9YWG2YIaIZyGalDWXe1BCennCQkX4Ah0rrieEBaJQIU4hVnI2612b6yhR69xnX0PGOyUbfOWVts+D9DBeBMWMw+s7wmsHgl41TXSuGwHG2YP4w98KpBT3e2/iOUszfOOrll6sJXppKN9aryBru59J+/x1sciLtTz63Q49H3Wes+nf4Fby59h2nPD+pJmln1QHnZ8VBWzXF8fig6SsZmrRFFz0b/kKdJSS3cVIp/NXCiXtR5/stPBpimuPkVZ/nGziRsUUGjhes9tjQD7ls4D7qRma41AIwPeS2jpO3rvMtvFO66CHHDdy95eQdnFTrODzB3sX94zLu9/3ruBziLuORctw9r+N5pKdYwm/y4m/VBn5113G5aPtuC+/OGzimTGXhp3Hdc5XSa/gF7xsD31fV+eiOWQfUzV3Hu/w2wQc7DPiQ+wrHL6mbEegivr+NeKfX/Si/pL7Jk03cR59GHBcol/+f1Veke7m/VmGl/A74d0yV6jP0s/yGl5X+cL7eUSXffFXl/8H3u/Pw4zO7Bztj7+Le7YdnY+9C7Zw8fnznZDfsnPwP488L21waSrUAAAAASUVORK5CYII=
As you can see, the readAsDataURL method DID encode your data to base64 but because it is used to produce Data URI , a string with format as data:[<MIME-type>][;charset=<encoding>][;base64], is added at the begin of your encoded data. This is the culprit cause 400 Bad Request error (Malformed multipart body).The solution is to eliminate this string before adding it to the request body:
parts.push(reader.result.replace(/^data:image\/(png|jpg);base64,/, ""));
I have tested and it works fine.

Related

Display a pdf file in the browser with sails

I have some documents in a directory and I want to show one embedded in the browser, I save the path of the document in a table and I can read the path from that table and download the document, but I can't figure out how to show the file in the browser.
I'm using the following code to send the file:
loadDocument: async function (req,res){
var SkipperDisk = require('skipper-disk');
var fileAdapter = SkipperDisk(/* optional opts */);
var fd = await Documents.find(
{
where: {id:'1'},
select: ['uploadFileFd']
}
).limit(1);
let uploadFileFd = fd[0]["uploadFileFd"];
var fileStream = fileAdapter.read(uploadFileFd);
fileStream.on('error', function (err){
return res.serverError(err);
});
res.contentType("application/pdf");
res.set("Content-disposition", "attachment; filename=" + "file"+ fd[0]["id"]+".pdf");
fileStream.pipe(res);
},
I want to call the function and load the pdf file in the browser, preferably without reloading all the page.
Clients browsers will download the pdf without trying to open the built-in PDF viewer (ie, Chrome) because of the Content-disposition: attachment header that you're sending - try using inline instead.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
res.setHeader('Content-type', 'application/pdf');
res.setHeader('Content-disposition', 'inline; filename="file' + fd[0]["id"] + '.pdf"');
I found a solution to my problem.
First I have to create a way to serve the static folder where the files are located, I found the answer here.
Then I modify the code to send the data encoded as base64 using 'base64-stream':
var readStream = fs.createReadStream(uploadFileFd);
readStream.on('open', function () {
readStream.pipe(new Base64Encode()).pipe(res);
});
readStream.on('error', function(err) {
res.end(err);
});
Finally I show the pdf file in the browser as follows:
.done(function(data){
var parent = $('embed#pdf').parent();
var newElement = "<embed src=data:application/pdf;base64,"+data+" id='pdf' width='100%' height='1200'>";
$('embed#pdf').remove();
parent.append(newElement);
})
Now I can display a pdf file in the browser embedded in my own page, thanks to all the people that try to help.

Microsoft cognitive services face API call

I've build an application on the Azure (microsoft) emotion API, but that was just merged with their cognitive services face API. I'm using a webcam to send an image (in binary data) to their server for analysis, and used to get an xml in return. (I've already commented out some old code, in this example. Trying to get it fixed).
function saveSnap(data){
// Convert Webcam IMG to BASE64BINARY to send to EmotionAPI
var file = data.substring(23).replace(' ', '+');
var img = Base64Binary.decodeArrayBuffer(file);
var ajax = new XMLHttpRequest();
// On return of data call uploadcomplete function.
ajax.addEventListener("load", function(event) {
uploadcomplete(event);
}, false);
// AJAX POST request
ajax.open("POST", "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=emotion","image/jpg");
ajax.setRequestHeader("Content-Type","application/json");
//ajax.setRequestHeader("Accept","text/html,application/xhtml+xml,application/xml");
ajax.setRequestHeader("Ocp-Apim-Subscription-Key","subscription_key");
ajax.send(img);
}
now I understood from their website the call returns a JSON. But I just can't get it to work. I can see there is data coming back, but how do I even get the JSON out of it. I'm probably missing something essential, and hope someone can help me out. :) the program was working when I could still use the Emotion API.
function uploadcomplete(event){
console.log("complete");
console.log(event);
//var xmlDoc = event.target.responseXML;
//var list = xmlDoc.getElementsByTagName("scores");
console.log(JSON.stringify(event));
A few issues to address:
You'll want to wait for the POST response, not just for the upload
completion.
You'll want to set the content type to be application/octet-stream if you are uploading a binary as you are.
You'll want to set the subscription key to the real value (you probably did before pasting your code here.)
.
function saveSnap(data) {
// Convert Webcam IMG to BASE64BINARY to send to EmotionAPI
var file = data.substring(23).replace(' ', '+');
var img = Base64Binary.decodeArrayBuffer(file);
ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if (ajax.readyState == XMLHttpRequest.DONE) {
console.log(JSON.stringify(ajax.response));
}
}
ajax.open('post', 'https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=emotion');
ajax.setRequestHeader('Content-Type', 'application/octet-stream');
ajax.setRequestHeader('Ocp-Apim-Subscription-Key', key);
ajax.send(img);
}

chrome PDF viewer can't download file

Here is my situation, I got a server running a PDF generator, when I make a request with some params, it will give me back a PDF file, the PDF is not stored in the server it's generated during the runtime.
Everything goes fine, I can get the PDF open in chrome's PDF viewer, but if want to download the file, an error occurred, like the image shows.
Because Chrome go to the origin URL to request the file, but the file is not a static resource on the server.
I don't know if anybody has run into this problem?
Whenever you leave the website you used to create the object URL (window.URL.createObjectURL(...)) that very object will get garbage collected. So you need to keep a reference to that object somehow.
This works for us in Chrome, Firefox, Safari, iOS Safari & Android to first display the PDF in capable browsers in a new tab and allow a download afterwards (in IE it just starts the download):
function openPdfInNewTab(url,
postData,
description = 'Document',
filename = description + '.pdf') {
if (!window.navigator.msSaveOrOpenBlob) {
var tabWindow = window.open('', '_blank');
var a = tabWindow.document.createElement('a');
a.textContent = 'Loading ' + description + '..';
tabWindow.document.body.appendChild(a);
tabWindow.document.body.style.cursor = 'wait';
} else {
spinnerService.show('html5spinner');
}
$http.post(url, postData, {responseType: 'arraybuffer'})
.then(function showDocument(response) {
var file = new Blob([response.data], {type: 'application/pdf'});
if (window.navigator.msSaveOrOpenBlob) {
spinnerService.hide('html5spinner');
window.navigator.msSaveOrOpenBlob(file, filename);
} else {
tabWindow.document.body.style.cursor = 'auto';
var url = a.href = window.URL.createObjectURL(file);
a.click();
a.download = filename;
}
$timeout(function revokeUrl() {
window.URL.revokeObjectURL(url);
}, 3600000);
}, handleDownloadError);
}
We have been opening PDFs in a new browser-tab and had similar issues.
For us it started working again when we use window.URL.createObjectURL instead of tabWindow.URL.createObject which displayed the PDF but didn't allow the download.
Chrome's built in PDF viewer will download the pdf file through the PDF's origin URL. So if the PDF is generated at server runtime and if it's not stored in the sever, the download could fail.
see link here: https://productforums.google.com/forum/#!topic/chrome/YxyVToLN8ho
Just as an additional comment:
We had the same problem on a project, on Chrome only.
Authenticated GET request would fetch the PDF as an attachment from API, and we would forward it via window.createObjectURL(blob) to the browser viewer.
The Network Error was due us invoking window.revokeObjectURL(url); after opening the PDF. When we removed that line, the blob wasn't garbage collected immediately after opening.
fetch(request)
.then(async response => {
if (!response.ok) {
return reject(response.statusText);
}
const blob = await response.blob();
const url = await window.URL.createObjectURL(blob);
window.open(url, '_blank');
URL.revokeObjectURL(url); // This line was causing the problem
resolve();
})
.catch(reject)

How would I read the file contents of a file in my app/extension's crx?

I want to include some files in my crx and then be able to read them in as data (into a string or Blob). How would I do this? Is there a way to use the FileSystem API for this?
chrome.runtime.getPackageDirectoryEntry was implemented on 2013-06-13, expected in Chrome 29:
Issue 177208: add read-only FileSystem API for access to packaged app/extension resources
Change: https://chromiumcodereview.appspot.com/16470003
Read file contents from crx via XHR is much more simple than FileSystem API:
var url = chrome.extension.getURL('the_file.txt'); // full url
var req = new XMLHttpRequest(); // read via XHR
req.open('GET', url);
req.onreadystatechange = function(e) {
if (req.readyState === 4 && req.status === 200) {
console.log(data);
} else {
// error
}
}
If you want to make the request in an injected context, you must have accessable resources declared in manifest.json first, list filename (support wildcards) in accessible resources entry.
"web_accessible_resources": [
"path_to_the_file.html",
"just_another_folder/*.txt"
]

Send Image File via XHR on Chrome

I'm using HTML5 drag&drop to get images from a user's computer and want to upload them to my Rails server (using Carrierwave on that end). I don't know exactly what I'm doing here, but cobbled together this code from these instructions http://code.google.com/p/html5uploader/wiki/HTML5Uploader
This returns a 500 error - can anyone take a look and help me out with what I'm doing wrong?
var files = e.dataTransfer.files;
if (files.length){
for (var i = 0; i<files.length; i++) {
var file = files[i];
var reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = function() {
var bin = reader.result;
var xhr = new XMLHttpRequest();
var boundary = 'xxxxxxxxx';
xhr.open('POST', '/images?up=true&base64=true', true);
xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary);
xhr.setRequestHeader('UP-FILENAME', file.name);
xhr.setRequestHeader('UP-SIZE', file.size);
xhr.setRequestHeader('UP-TYPE', file.type);
xhr.send(window.btoa(bin));
};
};
};
There are a couple of things that could be the culprit. You're reading the file as a binary string, then creating a multipart request, then sending a base64 encoded value.
There's no need to read the file or mess with base64 encoding. Instead, just construct a FormData object, append the file, and send that directly using xhr.send(formData). See my response here.