How to replace the deprecated BlobBuilder with the new Blob constructor? - javascript

Since Blobbuilder is deprecated and I have recently decided to use a new facial recognition API I am having a hard time switching over to just "blob".
function dataURItoBlob(dataURI, callback) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs
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 an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
var BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder;
var bb = new BlobBuilder();
bb.append(ab);
return bb.getBlob(mimeString);
}
I tried switching it over to just:
// write the ArrayBuffer to a blob, and you're done
var Blob = window.URL || window.webkitURL;
var bb = new Blob();
/*var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = window.URL.createObjectURL(blob);
document.body.appendChild(link);*/
/*var BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder;
var bb = new BlobBuilder();
bb.append(ab);*/
return bb.getBlob(mimeString);
}
But i keep getting Uncaught TypeError: Object #<URL> has no method 'getBlob' in the console. Not sure what I am missing. If i try to use bb.append(ab); I get Uncaught TypeError: Object #<Blob> has no method 'append' in the console.

Switching from BlobBuilder to Blob is quite straightforward. Try the following backwards-compatible code (the stuff in the catch block is your original code):
...
try {
return new Blob([ab], {type: mimeString});
} catch (e) {
// The BlobBuilder API has been deprecated in favour of Blob, but older
// browsers don't know about the Blob constructor
// IE10 also supports BlobBuilder, but since the `Blob` constructor
// also works, there's no need to add `MSBlobBuilder`.
var BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder;
var bb = new BlobBuilder();
bb.append(ab);
return bb.getBlob(mimeString);
}
}

Blob = (function() {
var nativeBlob = Blob;
// Add unprefixed slice() method.
if (Blob.prototype.webkitSlice) {
Blob.prototype.slice = Blob.prototype.webkitSlice;
}
else if (Blob.prototype.mozSlice) {
Blob.prototype.slice = Blob.prototype.mozSlice;
}
// Temporarily replace Blob() constructor with one that checks support.
return function(parts, properties) {
try {
// Restore native Blob() constructor, so this check is only evaluated once.
Blob = nativeBlob;
return new Blob(parts || [], properties || {});
}
catch (e) {
// If construction fails provide one that uses BlobBuilder.
Blob = function (parts, properties) {
var bb = new (WebKitBlobBuilder || MozBlobBuilder), i;
for (i in parts) {
bb.append(parts[i]);
}
return bb.getBlob(properties && properties.type ? properties.type : undefined);
};
}
};
}());
Include this before you are going to use Blobs and you'll be able to use Blob constructors in browsers that only support the deprecated BlobBuilder.

Related

ie11 Downloading Base64 documents

I have tried pretty much everything at this point and I cannot get anything to work in ie.
I need ie to download base64 documents from an attachment panel. I have no access to the server side code or database. The images cannot be stored in a folder to be pulled up, they need to be presented this way.
I have tried using a plain link and sticking the base64 sting in there and it just opens up a new blank window.
<a target="_blank" download class="btn btn-primary downloadAttachment" href="' + blobUrl + '" >Download</a>
I have tried turning the url into a blob and opening the blob and that resulted in the browser not doing anything.
function base64toBlob(base64Data, contentType) {
contentType = contentType || '';
var sliceSize = 1024;
var byteCharacters = base64Data;
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0 ; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, { type: contentType });
}
I am completely and totally stuck. I have tried everything from google and on here.
My two latest attempts here
https://jsfiddle.net/pqhdce2L/
http://jsfiddle.net/VB59f/464/
Some time ago I've coined this function to make ("offer/initialize") a download of an xlsx or csv content accepting both a Blob or a base64 string:
// Initializes a file download of a provided content
//
// Not usable outside browser (depends on window & document)
//
// #param {Blob|base64} cont File content as blob or base64 string
// #param {string} ftype File type (extension)
// #param {string} [fname='export.' + ftype] File name
// #param {string} [mime='application/zip'] File mime type
// #returns {void}
function makeFileDownload(cont, ftype, fname, mime) {
if (!fname) fname = 'export.' + ftype;
if (!mime) mime = ftype === 'csv' ? 'text/csv' : 'application/zip'; // or 'application/vnd.ms-excel'
if (Object.prototype.toString.call(cont) === '[object Blob]'
&& window.navigator && window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(cont, fname);
}
else {
var downloadLink = document.createElement('a');
downloadLink.download = fname;
downloadLink.href = typeof cont === 'string'
? 'data:' + mime + ';base64,' + cont
: window.URL.createObjectURL(cont);
downloadLink.onclick = function(e) { document.body.removeChild(e.target); };
downloadLink.style.display = 'none';
document.body.appendChild(downloadLink);
downloadLink.click();
}
};
This should be able to accept both Blob and base64 string - you should get the idea how it's being done for either a Blob and a base64 string from the if/else block.
If passing it base64 string is problematic just convert it to a Blob first (as suggested for example in this SO question, this answer is specifically aimed at IE11). Adjust the mime defaults according to your expected usage.
I suppose you already have the content (Blob/base64), keep your original link (which I suppose is to be clicked by an user) a plain link or rather a button (i.e. without the download/href attributes), attach it a click event handler where you'll call the function and it should initialize the download for you:
document.querySelector('#originalLink').addEventListener('click', function () {
makeFileDownload(content, extension, filename, mimetype);
});
If you are trying to generate blob URL in IE, it will not work.
We have to download the file to local by using below code:
function printPdf(id) {
$.ajax({
url: 'url',
type: 'POST',
data: { 'ID': id },
success: function (result) {
var blob = pdfBlobConvesion(result.PdfUrl, 'application/pdf');
var isIE = /*#cc_on!#*/false || !!document.documentMode;
// Edge 20+
var isEdge = !isIE && !!window.StyleMedia;
if (isIE || isEdge) {
window.navigator.msSaveOrOpenBlob(blob, "ProviderOfficePDF.pdf");
}
else {
var blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, "_blank");
}
}
});
}
function pdfBlobConvesion(b64Data, contentType) {
contentType = contentType || '';
var sliceSize = 512;
b64Data = b64Data.replace(/^[^,]+,/, '');
b64Data = b64Data.replace(/\s/g, '');
var byteCharacters = window.atob(b64Data);
var byteArrays = [];
for ( var offset = 0; offset < byteCharacters.length; offset = offset + sliceSize ) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, { type: contentType });
return blob;
}
IE, in classic fashion, requires you to use a proprietary method for "saving" a blob.
msSaveBlob or msSaveOrOpenBlob is what you're looking for.
Instead of adding it as the href, add an onclick handler to your a tag and call navigator.msSaveBlob(blob, "Sample Name");
Additionally if you need to support other browsers you'll need some other code to support those browsers.
For example:
var content = new Blob(["Hello world!"], { type: 'text/plain' });
var btn = document.getElementById('btn');
if (navigator.msSaveBlob) {
btn.onclick = download;
} else {
btn.href = URL.createObjectURL(content);
btn.download = true;
}
function download() {
if (navigator.msSaveBlob) {
navigator.msSaveBlob(content, "sample.txt");
}
}
<a id="btn" href="#">Download the text!</a>
var data = item;
var fileName = name;
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // IE
workaround
var byteCharacters = atob(data);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var blob = new Blob([byteArray], {type: 'application/octet-stream'});
window.navigator.msSaveOrOpenBlob(blob, fileName);
}
else if( agent.indexOf('firefox') > -1)
{
console.log(extention,'item111')
var byteCharacters = atob(data);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
//var FileSaver = require('file-saver');
var blob = new Blob([byteArray], {type: "application/octet-stream"});
saveAs(blob, fileName);
}
else{
this.fileDownload='data:application/octet-stream;base64,'+item;
var link = document.createElement("a");
const fileName=name;
link.href = this.fileDownload;
link.download=fileName;
link.click();
}
}

Send Uri form image to server and save as image [duplicate]

I've been trying to re-implement an HTML5 image uploader like the one on the Mozilla Hacks site, but that works with WebKit browsers. Part of the task is to extract an image file from the canvas object and append it to a FormData object for upload.
The issue is that while canvas has the toDataURL function to return a representation of the image file, the FormData object only accepts File or Blob objects from the File API.
The Mozilla solution used the following Firefox-only function on canvas:
var file = canvas.mozGetAsFile("foo.png");
...which isn't available on WebKit browsers. The best solution I could think of is to find some way to convert a Data URI into a File object, which I thought might be part of the File API, but I can't for the life of me find something to do that.
Is it possible? If not, any alternatives?
After playing around with a few things, I managed to figure this out myself.
First of all, this will convert a dataURI 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});
}
From there, appending the data to a form such that it will be uploaded as a file is easy:
var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
BlobBuilder and ArrayBuffer are now deprecated, here is the top comment's code updated with Blob constructor:
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'});
}
This one works in iOS and Safari.
You need to use Stoive's ArrayBuffer solution but you can't use BlobBuilder, as vava720 indicates, so here's the mashup of both.
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' });
}
Firefox has canvas.toBlob() and canvas.mozGetAsFile() methods.
But other browsers do not.
We can get dataurl from canvas and then convert dataurl to blob object.
Here is my dataURLtoBlob() function. It's very short.
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});
}
Use this function with FormData to handle your canvas or dataurl.
For example:
var dataurl = canvas.toDataURL('image/jpeg',0.8);
var blob = dataURLtoBlob(dataurl);
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");
Also, you can create a HTMLCanvasElement.prototype.toBlob method for non gecko engine browser.
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);
};
}
Now canvas.toBlob() works for all modern browsers not only Firefox.
For example:
canvas.toBlob(
function(blob){
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");
//continue do something...
},
'image/jpeg',
0.8
);
My preferred way is canvas.toBlob()
But anyhow here is yet another way to convert base64 to a blob using fetch ^^,
var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
fetch(url)
.then(res => res.blob())
.then(blob => {
var fd = new FormData()
fd.append('image', blob, 'filename')
console.log(blob)
// Upload
// fetch('upload', {method: 'POST', body: fd})
})
Thanks to #Stoive and #vava720 I combined the two in this way, avoiding to use the deprecated BlobBuilder and ArrayBuffer
function dataURItoBlob(dataURI) {
'use strict'
var byteString,
mimestring
if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
byteString = atob(dataURI.split(',')[1])
} else {
byteString = decodeURI(dataURI.split(',')[1])
}
mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]
var content = new Array();
for (var i = 0; i < byteString.length; i++) {
content[i] = byteString.charCodeAt(i)
}
return new Blob([new Uint8Array(content)], {type: mimestring});
}
The evolving standard looks to be canvas.toBlob() not canvas.getAsFile() as Mozilla hazarded to guess.
I don't see any browser yet supporting it :(
Thanks for this great thread!
Also, anyone trying the accepted answer should be careful with BlobBuilder as I'm finding support to be limited (and namespaced):
var bb;
try {
bb = new BlobBuilder();
} catch(e) {
try {
bb = new WebKitBlobBuilder();
} catch(e) {
bb = new MozBlobBuilder();
}
}
Were you using another library's polyfill for BlobBuilder?
var BlobBuilder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);
can be used without the try catch.
Thankx to check_ca. Great work.
The original answer by Stoive is easily fixable by changing the last line to accommodate Blob:
function dataURItoBlob (dataURI) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs
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 an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
return new Blob([ab],{type: mimeString});
}
Here is an ES6 version of Stoive's answer:
export class ImageDataConverter {
constructor(dataURI) {
this.dataURI = dataURI;
}
getByteString() {
let byteString;
if (this.dataURI.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(this.dataURI.split(',')[1]);
} else {
byteString = decodeURI(this.dataURI.split(',')[1]);
}
return byteString;
}
getMimeString() {
return this.dataURI.split(',')[0].split(':')[1].split(';')[0];
}
convertToTypedArray() {
let byteString = this.getByteString();
let ia = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return ia;
}
dataURItoBlob() {
let mimeString = this.getMimeString();
let intArray = this.convertToTypedArray();
return new Blob([intArray], {type: mimeString});
}
}
Usage:
const dataURL = canvas.toDataURL('image/jpeg', 0.5);
const blob = new ImageDataConverter(dataURL).dataURItoBlob();
let fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
Thanks! #steovi for this solution.
I have added support to ES6 version and changed from unescape to dataURI(unescape is deprecated).
converterDataURItoBlob(dataURI) {
let byteString;
let mimeString;
let ia;
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(dataURI.split(',')[1]);
} else {
byteString = encodeURI(dataURI.split(',')[1]);
}
// separate out the mime component
mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
make it simple :D
function dataURItoBlob(dataURI,mime) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs
var byteString = window.atob(dataURI);
// separate out the mime component
// write the bytes of the string to an ArrayBuffer
//var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
var blob = new Blob([ia], { type: mime });
return blob;
}
toDataURL gives you a string and you can put that string to a hidden input.
I had exactly the same problem as Ravinder Payal, and I've found the answer. Try this:
var dataURL = canvas.toDataURL("image/jpeg");
var name = "image.jpg";
var parseFile = new Parse.File(name, {base64: dataURL.substring(23)});

URL.createObjectURL returns a blob with null prepended. Eg : Blob:null/12415-63

blob has null prepended at the start eg : blob:null/72438-4637-23673-34721. But when I use the same as a src to the , it shows up the correct image.
I am using URL.createObjectURL call. I also tried using webkitURL. Both return a blob with null appended at the start. Also the blob value returned by URL and webkitURL are not the same.
Here is the code snippet
var dataUrl = canvas.toDataURL();
// The last piece of the data URL after the comma
var byteString = atob(dataUrl.split(',')[1]);
// Populate an array buffer
var arrayBuffer = new ArrayBuffer(byteString.length);
var uint8Array = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteString.length; i++) {
uint8Array[i] = byteString.charCodeAt(i);
}
var blob = new Blob([uint8Array], { type: "image/png" });
var blobVal = URL.createObjectURL(blob);
Here the blobVal has "blob:null/1234-5678-9012- ..."
That's the origin, file system and sandboxed iframes(maybe others) have null as their origin. If you set up a local sever it should say http%3A//localhost, that's http://localhost url encoded
var arrayBuffer = new ArrayBuffer(100);
var uint8Array = new Uint8Array(arrayBuffer);
for (var i = 0; i < 100; i++) {
uint8Array[i] = i;
}
var blob = new Blob([uint8Array], { type: "image/png" });
var blobVal = URL.createObjectURL(blob);
$('div').html(blobVal);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
This one sais http%3A//fiddle.jshell.net
http://jsfiddle.net/thsn3ayp/

Empty files uploaded in Android Native browser

I'm creating a website for mobile phones that resizes photos and uploads them.
$('#ImgPreview canvas').each(function(pIndex) {
vFormData.append(pIndex, canvasToJpegBlob($(this)[0]), vIssueId +'-attachment0'+ pIndex +'.jpg');
});
$.ajax({
url: '/api/ob/issuefileupload',
data: vFormData,
processData: false,
contentType: false,
type: 'POST'
}).done(function(pData) {
window.location = '/issue?id='+ vIssueId;
}).fail(function(pJqXHR) {
alert(My.Strings.UploadFailed);
});
This works in Chrome for Android and in Safari on iOS, but in the native Android browser, the files have a content-length of 0 and name Blob + a UID. When the file is added to the formdata the size also seems rather large (900k opposed to 50k in Chrome).
The canvasToJpegBlob function:
function canvasToJpegBlob(pCanvas) {
var vMimeType = "image/jpeg",
vDataURI,
vByteString,
vBlob,
vArrayBuffer,
vUint8Array, i,
vBlobBuilder;
vDataURI = pCanvas.toDataURL(vMimeType, 0.8);
vByteString = atob(vDataURI.split(',')[1]);
vArrayBuffer = new ArrayBuffer(vByteString.length);
vUint8Array = new Uint8Array(vArrayBuffer);
for (i = 0; i < vByteString.length; i++) {
vUint8Array[i] = vByteString.charCodeAt(i);
}
try {
vBlob = new Blob([vUint8Array.buffer], {type : vMimeType});
} catch(e) {
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if (e.name === 'TypeError' && window.BlobBuilder) {
vBlobBuilder = new BlobBuilder();
vBlobBuilder.append(vUint8Array.buffer);
vBlob = vBlobBuilder.getBlob(vMimeType);
} else if (e.name === 'InvalidStateError') {
vBlob = new Blob([vUint8Array.buffer], {type : vMimeType});
} else {
alert(My.Strings.UnsupportedFile);
}
}
return vBlob;
}
Is there any way to get this working in the native Android browser?
I also ran into this problem and needed to come up with a more generic solution as in some cases I won't have control over the server-side code.
Eventually I reached a solution that is almost completely transparent. The approach was to polyfill the broken FormData with a blob that appends data in the necessary format for multipart/form-data. It overrides XHR's send() with a version that reads the blob into a buffer that gets sent in the request.
Here's the main code:
var
// Android native browser uploads blobs as 0 bytes, so we need a test for that
needsFormDataShim = (function () {
var bCheck = ~navigator.userAgent.indexOf('Android')
&& ~navigator.vendor.indexOf('Google')
&& !~navigator.userAgent.indexOf('Chrome');
return bCheck && navigator.userAgent.match(/AppleWebKit\/(\d+)/).pop() <= 534;
})(),
// Test for constructing of blobs using new Blob()
blobConstruct = !!(function () {
try { return new Blob(); } catch (e) {}
})(),
// Fallback to BlobBuilder (deprecated)
XBlob = blobConstruct ? window.Blob : function (parts, opts) {
var bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder);
parts.forEach(function (p) {
bb.append(p);
});
return bb.getBlob(opts ? opts.type : undefined);
};
function FormDataShim () {
var
// Store a reference to this
o = this,
// Data to be sent
parts = [],
// Boundary parameter for separating the multipart values
boundary = Array(21).join('-') + (+new Date() * (1e16*Math.random())).toString(36),
// Store the current XHR send method so we can safely override it
oldSend = XMLHttpRequest.prototype.send;
this.append = function (name, value, filename) {
parts.push('--' + boundary + '\nContent-Disposition: form-data; name="' + name + '"');
if (value instanceof Blob) {
parts.push('; filename="'+ (filename || 'blob') +'"\nContent-Type: ' + value.type + '\n\n');
parts.push(value);
}
else {
parts.push('\n\n' + value);
}
parts.push('\n');
};
// Override XHR send()
XMLHttpRequest.prototype.send = function (val) {
var fr,
data,
oXHR = this;
if (val === o) {
// Append the final boundary string
parts.push('--' + boundary + '--');
// Create the blob
data = new XBlob(parts);
// Set up and read the blob into an array to be sent
fr = new FileReader();
fr.onload = function () { oldSend.call(oXHR, fr.result); };
fr.onerror = function (err) { throw err; };
fr.readAsArrayBuffer(data);
// Set the multipart content type and boudary
this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
XMLHttpRequest.prototype.send = oldSend;
}
else {
oldSend.call(this, val);
}
};
}
And just use it like so, calling fd.append(name, value) as normal afterwards:
var fd = needsFormDataShim ? new FormDataShim() : new FormData();
How about trying to draw it on canvas, using matrix to scale it to the size you wish and then sending it to server using canvas.toDataURL. Check out this question.
i use this to fix the problem:
// not use blob, simply use key value
var form = new FormData();
// get you content type and raw data from data url
form.append( 'content_type', type);
form.append( 'content', raw);

Convert Data URI to File then append to FormData

I've been trying to re-implement an HTML5 image uploader like the one on the Mozilla Hacks site, but that works with WebKit browsers. Part of the task is to extract an image file from the canvas object and append it to a FormData object for upload.
The issue is that while canvas has the toDataURL function to return a representation of the image file, the FormData object only accepts File or Blob objects from the File API.
The Mozilla solution used the following Firefox-only function on canvas:
var file = canvas.mozGetAsFile("foo.png");
...which isn't available on WebKit browsers. The best solution I could think of is to find some way to convert a Data URI into a File object, which I thought might be part of the File API, but I can't for the life of me find something to do that.
Is it possible? If not, any alternatives?
After playing around with a few things, I managed to figure this out myself.
First of all, this will convert a dataURI 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});
}
From there, appending the data to a form such that it will be uploaded as a file is easy:
var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
BlobBuilder and ArrayBuffer are now deprecated, here is the top comment's code updated with Blob constructor:
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'});
}
This one works in iOS and Safari.
You need to use Stoive's ArrayBuffer solution but you can't use BlobBuilder, as vava720 indicates, so here's the mashup of both.
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' });
}
Firefox has canvas.toBlob() and canvas.mozGetAsFile() methods.
But other browsers do not.
We can get dataurl from canvas and then convert dataurl to blob object.
Here is my dataURLtoBlob() function. It's very short.
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});
}
Use this function with FormData to handle your canvas or dataurl.
For example:
var dataurl = canvas.toDataURL('image/jpeg',0.8);
var blob = dataURLtoBlob(dataurl);
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");
Also, you can create a HTMLCanvasElement.prototype.toBlob method for non gecko engine browser.
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);
};
}
Now canvas.toBlob() works for all modern browsers not only Firefox.
For example:
canvas.toBlob(
function(blob){
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");
//continue do something...
},
'image/jpeg',
0.8
);
My preferred way is canvas.toBlob()
But anyhow here is yet another way to convert base64 to a blob using fetch ^^,
var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
fetch(url)
.then(res => res.blob())
.then(blob => {
var fd = new FormData()
fd.append('image', blob, 'filename')
console.log(blob)
// Upload
// fetch('upload', {method: 'POST', body: fd})
})
Thanks to #Stoive and #vava720 I combined the two in this way, avoiding to use the deprecated BlobBuilder and ArrayBuffer
function dataURItoBlob(dataURI) {
'use strict'
var byteString,
mimestring
if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
byteString = atob(dataURI.split(',')[1])
} else {
byteString = decodeURI(dataURI.split(',')[1])
}
mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]
var content = new Array();
for (var i = 0; i < byteString.length; i++) {
content[i] = byteString.charCodeAt(i)
}
return new Blob([new Uint8Array(content)], {type: mimestring});
}
The evolving standard looks to be canvas.toBlob() not canvas.getAsFile() as Mozilla hazarded to guess.
I don't see any browser yet supporting it :(
Thanks for this great thread!
Also, anyone trying the accepted answer should be careful with BlobBuilder as I'm finding support to be limited (and namespaced):
var bb;
try {
bb = new BlobBuilder();
} catch(e) {
try {
bb = new WebKitBlobBuilder();
} catch(e) {
bb = new MozBlobBuilder();
}
}
Were you using another library's polyfill for BlobBuilder?
var BlobBuilder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);
can be used without the try catch.
Thankx to check_ca. Great work.
The original answer by Stoive is easily fixable by changing the last line to accommodate Blob:
function dataURItoBlob (dataURI) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs
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 an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
return new Blob([ab],{type: mimeString});
}
Here is an ES6 version of Stoive's answer:
export class ImageDataConverter {
constructor(dataURI) {
this.dataURI = dataURI;
}
getByteString() {
let byteString;
if (this.dataURI.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(this.dataURI.split(',')[1]);
} else {
byteString = decodeURI(this.dataURI.split(',')[1]);
}
return byteString;
}
getMimeString() {
return this.dataURI.split(',')[0].split(':')[1].split(';')[0];
}
convertToTypedArray() {
let byteString = this.getByteString();
let ia = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return ia;
}
dataURItoBlob() {
let mimeString = this.getMimeString();
let intArray = this.convertToTypedArray();
return new Blob([intArray], {type: mimeString});
}
}
Usage:
const dataURL = canvas.toDataURL('image/jpeg', 0.5);
const blob = new ImageDataConverter(dataURL).dataURItoBlob();
let fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
Thanks! #steovi for this solution.
I have added support to ES6 version and changed from unescape to dataURI(unescape is deprecated).
converterDataURItoBlob(dataURI) {
let byteString;
let mimeString;
let ia;
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(dataURI.split(',')[1]);
} else {
byteString = encodeURI(dataURI.split(',')[1]);
}
// separate out the mime component
mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
make it simple :D
function dataURItoBlob(dataURI,mime) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs
var byteString = window.atob(dataURI);
// separate out the mime component
// write the bytes of the string to an ArrayBuffer
//var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
var blob = new Blob([ia], { type: mime });
return blob;
}
toDataURL gives you a string and you can put that string to a hidden input.
I had exactly the same problem as Ravinder Payal, and I've found the answer. Try this:
var dataURL = canvas.toDataURL("image/jpeg");
var name = "image.jpg";
var parseFile = new Parse.File(name, {base64: dataURL.substring(23)});

Categories