Problem
I’ve been trying to re-implement an HTML5 picture uploader that works with WebKit browsers, like the one on the Mozilla Hacks site. Extracting an image file from the canvas object and appending it to a FormData object for upload is part of the task.
The problem is that, while the toDataURL function on canvas returns an image file representation, the FormData object only takes File or Blob objects from the File API.
On canvas, Mozilla’s solution employed the following Firefox-only function:
var file = canvas.mozGetAsFile("foo.png");
…which aren’t supported by WebKit browsers. The only answer I can think of is to discover a mechanism to transform a Data URI into a File object, which I assumed was part of the File API, but I can’t find anything that does it.
Is that even possible? If not, are there any other options?
Thanks.
Asked by Stoive
Solution #1
After experimenting with a few things, I was able to figure it out on my own.
To begin, a dataURI will be converted to a Blob:
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
It’s then a simple matter of attaching the data to a form so that it can be published as a file:
var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
Answered by Stoive
Solution #2
BlobBuilder and ArrayBuffer are now deprecated; here is the revised code from the top comment:
function dataURItoBlob(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
}
Answered by vava720
Solution #3
This one is compatible with both iOS and Safari.
You’ll need Stoive’s ArrayBuffer solution, but you won’t be able to utilize BlobBuilder, as vava720 suggests, so here’s a mix of the two.
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: 'image/jpeg' });
}
Answered by William T.
Solution #4
Canvas.toBlob() and canvas.mozGetAsFile() are two methods in Firefox.
Other browsers, on the other hand, do not.
Dataurl can be obtained from canvas and then converted to a blob object.
My dataURLtoBlob() function is shown below. It’s only a few sentences long.
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
To handle your canvas or dataurl, use this function with FormData.
For example:
var dataurl = canvas.toDataURL('image/jpeg',0.8);
var blob = dataURLtoBlob(dataurl);
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");
You can also build an HTMLCanvasElement.prototype.toBlob method for browsers that don’t use the Gecko engine.
if(!HTMLCanvasElement.prototype.toBlob){
HTMLCanvasElement.prototype.toBlob = function(callback, type, encoderOptions){
var dataurl = this.toDataURL(type, encoderOptions);
var bstr = atob(dataurl.split(',')[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
var blob = new Blob([u8arr], {type: type});
callback.call(this, blob);
};
}
Not only Firefox, but all current browsers now support canvas.toBlob(). Consider the following scenario:
canvas.toBlob(
function(blob){
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");
//continue do something...
},
'image/jpeg',
0.8
);
Answered by cuixiping
Solution #5
Canvas is my favored method. toBlob()
But, in any case, here’s another way to convert base64 to a blob with fetch:
Answered by Endless
Post is based on https://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata