Is Firefox the only that supports the sendAsBinary method?
At the moment, I believe only FF3+ supports this, though there is a workaround for Chrome.
The links around http://code.google.com/p/chromium/issues/detail?id=35705 are very confusing, but I do not think there is any workaround on Chrome 8 for POST'ing binary data.
You can convert the data to base64 and upload that, but then the server has to be able to decode it.
Chrome 9 (currently in Dev channel, not even Beta yet) lets you do XmlHttpRequest.send(blob) where the blob's bytes are sent as-is (not converted to utf-8), so the non-standard XmlHttpRequest.sendAsBinary() is not necessary for binary file uploads.
You must create this blob from the "binary" string that is in evt.target.result after a successful FileReader.readAsBinaryString(). That requires using ArrayBuffer and Uint8Array, which are not available in Chrome 8.
As far as I know, yes, only Firefox supports it. It's not part of the W3C standard, so there's no guarantee that it'll ever be supported by any other browser.
I had same error, but I'm also using Prototype.js. Seems it has some replacement for map function and it were throwing TypeError for me Object ..file data here.. has no method 'each'
So i used this replacement instead
//fix sendAsBinary for chrome
try {
if (typeof XMLHttpRequest.prototype.sendAsBinary == 'undefined') {
XMLHttpRequest.prototype.sendAsBinary = function(text){
var data = new ArrayBuffer(text.length);
var ui8a = new Uint8Array(data, 0);
for (var i = 0; i < text.length; i++) ui8a[i] = (text.charCodeAt(i) & 0xff);
this.send(ui8a);
}
}
} catch (e) {}
The workaround for Chrome is explained at the following URL:
http://code.google.com/p/chromium/issues/detail?id=35705
Related
I have three failing versions of the following code in a chrome extension, which attempts to intercept a click to a link pointing to a pdf file, fetch that file, convert it to base64, and then log it. But I'm afraid I don't really know anything about binary formats and encodings, so I'm royally sucking this up.
var links = document.getElementsByTagName("a");
function transform(blob) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(blob)));
};
function getlink(link) {
var x = new XMLHttpRequest();
x.open("GET", link, true);
x.responseType = 'blob';
x.onload = function(e) {
console.log("Raw response:");
console.log(x.response);
console.log("Direct transformation:");
console.log(btoa(x.response));
console.log("Mysterious thing I got from SO:");
console.log(transform(x.response));
window.location.href = link;
};
x.onerror = function (e) {
console.error(x.statusText);
};
x.send(null);
};
for (i = 0, len = links.length; i < len; i++) {
var l = links[i]
l.addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
getlink(this.href);
}, false);
};
Version 1 doesn't have the call to x.responseType, or the call to transform. It was my original, naive, implementation. It threw an error: "The string to be encoded contains characters outside of the Latin1 range."
After googling that error, I found this prior SO, which suggests that in parsing an image:
The response type needs to be set to blob. So this code does that.
There's some weird line, I don't know what it does at all: String.fromCharCode.apply(null, new Uint8Array(blob)).
Because I know nothing about binary formats, I guessed, probably stupidly, that making a PDF base64 would be the same as making some random image format base64. So, in fine SO tradition, I copied code that I don't really understand. In stages.
Version 2 of the code just set the response type to blob but didn't try the second transformation. And the code worked, and logged something that looked like a base64 string, but a clearly incorrect string. In its entirety, it logged:
W29iamVjdCBCbG9iXQ==
Which is just goofily wrong. It's obviously too short for a 46k pdf file, and a reference base64 encoding I created with python from the commandline was much much much longer, as one would expect.
Version 3 of the code then also applies the mysterious transformation using stringFromCharCode and all the rest, which I shoved into the transform function.
However, that doesn't log anything at all---a blank line appears in the console in its appropriate place. No errors, no nonsense output, just a blank line.
I know I'm getting the correct file from prior testing. Also, the call to log the raw response object produces Blob {size: 45587, type: "application/pdf"}, which is the correct filesize for the pdf I'm experimenting with, so the blob actually contains what it should when it gets into the browser.
I'm using, and only need to support, a current version of chrome.
Can someone tell me what I'm doing wrong?
Thanks!
If you only need to support modern browsers, you should also be able to use FileReader#readAsDataURL.
That would let you do something like this:
var reader = new FileReader();
reader.addEventListener("load", function () {
console.log(reader.result);
}, false);
// The function accepts Blobs and Files
reader.readAsDataURL(x.response);
This logs a data URI, which will contain your base64 data.
I think I've found my own solution. The response type needs to be arraybuffer not blob.
I've been using Blob of HTML5 to consolidate a bunch of data including files and strings. Since the files and strings to be sent are not pre-specified in program, and I need to pack all data in a JS file and send them immediately, so I use Array of javascript to collect available data, then make this array as a parameter of Blob constructor. It works fine in Chrome and Firefox, but throws a javascript error when using IE11.
Unhandled exception at line 161, column 9 in ##$%.js
0x800a139e - JavaScript Runtime Error: InvalidStateError
My code is as follows:
var blobPackage_array = [];
if(userType != null)
blobPackage_array.push(userType);
if(userInfo != null)
blobPackage_array.push(userInfo);
for (var i = 0; i < fileList.length; i++) {
blobPackage_array.push(fileList[i]);
}
var blobPackage = new Blob(blobPackage_array); //throw javascript runtime error
I previously suspected that IE doesn't support Blob, so I tested this:
var blobPackage = new Blob(["test", fileList[0]]);
It worked fine, no error. My last guess is that IE doesn't recognise blobPackage_array as a valid parameter of Blob constructor. But Blob doesn't have a append method, meanwhile I can not know how many files that need to be appended, which means I can not construct a Blob once and for all.
Anyone ever encounter this? anything I can use to bypass this? I'd appreciate any suggestion.
Update! For some reason, I can not use FormData instead, It has to be blob...
anybody can help me on this?
Update again! Thanks to your kind reply, there are some progress. I checked MSDN, Blob's constructor should be like this: var blobObject = new Blob([new Uint8Array(array)], { type: 'image/png' });. I tried to construct a Uint8Array with blobPackage_array by this var uint8array = new Uint8Array(blobPackage_array);. I find that data is lost while this transformation. But in fact, var blobPackage = new Blob([uint8array]); can work, without errors. Thus I just need to fix the transformation problem.
I figured this out. I'm such an idiot..IE do not recognize my original blobPackage_array as a valid parameter because of those variables I append:
if(userType != null)
blobPackage_array.push(userType);
I just need to validate userType by this:
if(userType != null)
blobPackage_array.push(new String(userType));
So, don't bother to transform all data to UInt8Array type...
I have a base64 pdf that I get from external system.
I want to be able to download this pdf in IE9 with JavaScript and this is a problem since IE9 doesn't support DATA URI for pdf.
Please help me.
Thanks!
You should use Adobe Flash based plugin Downloadify (see the demo) to allow users download file in IE9.
You may check if the current browser supports dataURI or not using the following js function:
function CheckDataURISupport(){
var result = true;
var checkDataURISupportImage = new Image();
checkDataURISupportImage.onload = checkDataURISupportImage.onerror = function(){
if(this.width != 1 || this.height != 1){
result = false;
}
}
checkDataURISupportImage.src = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
// check if we have datauri support in current browser - end
return result;
}
Downloadify.js is a great solution for your case. Just be careful with options. There should be
'dataType': 'base64'
'data:' string representation of pdf in base64 format
Also be sure that your link/button has inserted 'flash code' by downloadify plugin (check source code after downloadify.create() initialization). Also you can check if your base64 has data:application/pdf;base64, type at the beginning.
I'm trying to find a cross browser way to store data locally in HTML5. I have generated a chunk of data in a Blob (see MDN). Now I want to move this Blob to the actual filesystem and save it locally. I've found the following ways to achieve this;
Use the <a download> attribute. This works only in Chrome currently.
Microsoft introduces a saveAs function in IE 10 which will achieve this.
Open the Blob URL in the browser and save it that way.
None of these seems to work in Safari though. While (1) works in Chrome, (2) in IE and (3) in Firefox no one works in Safari 6. The download attribute is not yet implemented and when trying to open a blob using the URL Safari complains that URLs starting with blob: are not valid URLs.
There is a good script that encapsulates (1) and (3) called FileSaver.js but that does not work using the latest Safari version.
Is there a way to save Blobs locally in a cross browser fashion?
FileSaver.js has beed updated recently and it works on IE10, Safari5+ etc.
See: https://github.com/eligrey/FileSaver.js/#supported-browsers
The file name sucks, but this works for me in Safari 8:
window.open('data:attachment/csv;charset=utf-8,' + encodeURI(csvString));
UPDATE: No longer working in Safari 9.x
The only solution that I have come up with is making a data: url instead. For me this looks like:
window.open("data:image/svg+xml," + encodeURIComponent(currentSVGString));
Here data is the array buffer data coming from response while making http rest call in js. This works in safari, however there might me some issue in filename as it comes to be untitled.
var binary = '';
var bytes = new Uint8Array(data);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
var base64 = 'data:' + contentType + ';base64,' + window.btoa(binary);
var uri = encodeURI(base64);
var anchor = document.createElement('a');
document.body.appendChild(anchor);
anchor.href = uri;
anchor.download = fileName;
anchor.click();
document.body.removeChild(anchor);
Have you read this article? http://updates.html5rocks.com/2012/06/Don-t-Build-Blobs-Construct-Them
Relating to http://caniuse.com/#search=blob, blobs are possible to use in safari.
You should consturct a servlet which delivers the blob via standard http:// url, so you can avoid using blob: url. Just make a request to that url and build your blob.
Afterwards you can save it in your filesystem or local storage.
The download attribute is supported since ~safari 10.1, so currently this is the way to go.
This is the only thing that worked for me on safari.
var newWindow = window.open();
const blobPDF = await renderMapPDF(); // Your async stuff goes here
if (!newWindow) throw new Error('Window could not be opened.');
newWindow.location = URL.createObjectURL(blobPDF);
I noticed a blog post from Google that mentions the ability to paste images directly from the clipboard into a Gmail message if you're using the latest version of Chrome. I tried this with my version of Chrome (12.0.742.91 beta-m) and it works great using control keys or the context menu.
From that behavior I need to assume that the latest version of webkit used in Chrome is able to deal with images in the Javascript paste event, but I have been unable to locate any references to such an enhancement. I believe ZeroClipboard binds to keypress events to trigger its flash functionality and as such wouldn't work through the context menu (also, ZeroClipboard is cross-browser and the post says this works only with Chrome).
So, how does this work and where the enhancement was made to Webkit (or Chrome) that enables the functionality?
I spent some time experimenting with this. It seems to sort of follow the new Clipboard API spec. You can define a "paste" event handler and look at event.clipboardData.items, and call getAsFile() on them to get a Blob. Once you have a Blob, you can use FileReader on it to see what's in it. This is how you can get a data url for the stuff you just pasted in Chrome:
document.onpaste = function (event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
console.log(JSON.stringify(items)); // might give you mime types
for (var index in items) {
var item = items[index];
if (item.kind === 'file') {
var blob = item.getAsFile();
var reader = new FileReader();
reader.onload = function (event) {
console.log(event.target.result); // data url!
};
reader.readAsDataURL(blob);
}
}
};
Once you have a data url you can display the image on the page. If you want to upload it instead, you could use readAsBinaryString, or you could put it into an XHR using FormData.
Edit: Note that the item is of type DataTransferItem. JSON.stringify might not work on the items list, but you should be able to get mime type when you loop over items.
The answer by Nick seems to need small changes to still work :)
// window.addEventListener('paste', ... or
document.onpaste = function (event) {
// use event.originalEvent.clipboard for newer chrome versions
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
console.log(JSON.stringify(items)); // will give you the mime types
// find pasted image among pasted items
var blob = null;
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") === 0) {
blob = items[i].getAsFile();
}
}
// load image if there is a pasted image
if (blob !== null) {
var reader = new FileReader();
reader.onload = function(event) {
console.log(event.target.result); // data url!
};
reader.readAsDataURL(blob);
}
}
Example running code: http://jsfiddle.net/bt7BU/225/
So the changes to nicks answer were:
var items = event.clipboardData.items;
to
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
Also I had to take the second element from the pasted items (first one seems to be text/html if you copy an image from another web page into the buffer). So I changed
var blob = items[0].getAsFile();
to a loop finding the item containing the image (see above)
I didn't know how to answer directly to Nick's answer, hope it is fine here :$ :)
As far as I know -
With HTML 5 features(File Api and the related) - accessing clipboard image data is now possible with plain javascript.
This however fails to work on IE (anything less than IE 10). Don't know much about IE10 support also.
For IE the optiens that I believe are the 'fallback' options are
either using Adobe's AIR api
or
using a signed applet