Blob from DataURL?
Asked Answered
F

11

144

Using FileReader's readAsDataURL() I can transform arbitrary data into a Data URL. Is there way to convert a Data URL back into a Blob instance using builtin browser apis?

Frag answered 28/8, 2012 at 23:21 Comment(0)
S
213

User Matt has proposed the following code a year ago ( How to convert dataURL to file object in javascript? ) which might help you

EDIT: As some commenters reported, BlobBuilder has been deprecated some time ago. This is the updated code:

function dataURItoBlob(dataURI) {
  // convert base64 to raw binary data held in a string
  // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
  var byteString = atob(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);

  // create a view into the buffer
  var ia = new Uint8Array(ab);

  // set the bytes of the buffer to the correct values
  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([ab], {type: mimeString});
  return blob;

}
Sailplane answered 6/9, 2012 at 12:41 Comment(11)
The duplicated split seems a bit wasteful, given that this could be a very large string.Partin
What is the role of the variable "ia" - its assigned a value but is never used in forming the blob. Why?Impolicy
Same question as @Blaze, why is the iteration over ia necessary? @SailplaneRapeseed
Blaze, Ward: ia is a view of the buffer. So when you set each byte in ia, it is writing into the buffer. Without the loop over ia the buffer will be completely empty.Principal
How do I find SO answer #6850276 ?Synchronous
This has some additional code for cases where the dataURI isn't encoding base64 data: gist.github.com/andipaetzold/d88f29caa0ddb3566759Synchronous
@BT : I guess https://mcmap.net/q/42032/-how-to-convert-dataurl-to-file-object-in-javascript-duplicate is intended (note the redirected URL includes "#6850276").Evelinaeveline
I think this should be var blob = new Blob([ia], ...);Ommiad
FWIW, we can create the ArrayBuffer more directly: Uint8Array.from(byteString, ch => ch.charCodeAt(0)).buffer.Faris
atob() is marked incorrectly as deprecated in vscode. window.atob() is a workaround.Holleyholli
NOTE: this code does not consider malformed URIs. You should first check whether the string starts with data: and just to be sure, try-catch it. Otherwise you'll crash your appLilytrotter
F
94

Like @Adria method but with Fetch api and just smaller [caniuse?]
Don't have to think about mimetype since blob response type just works out of the box

Warning: Can violate the Content Security Policy (CSP)
...if you use that stuff

var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="

fetch(url)
.then(res => res.blob())
.then(blob => console.log(blob))

Don't think you could do it any smaller then this without using lib's

Frazil answered 23/3, 2016 at 16:16 Comment(5)
Very elegant. Unfortunately edge 13 and safari doesn't support "fetch".Chloroform
It is supported in edge 14 and safari 10.1Frazil
Async version: const blob = await (await fetch(url)).blob();Incoming
Works great, but the file doesn't have an extension when read from server. Any thoughts?Thingumajig
The bigger problem of this fetch is it force to having a async API.October
M
53

In modern browsers one can use the one liner suggested by Christian d'Heureuse in a comment:

const blob = await (await fetch(dataURI)).blob(); 
Mandle answered 1/7, 2020 at 13:0 Comment(2)
One caveat with this approach is that this could violate your content security policy (CSP), if your application has any.Guntar
Adding to what @Guntar commented, you'll have to add connect-src data: to your Content-Security-PolicyRobby
W
21
dataURItoBlob : function(dataURI, dataTYPE) {
        var binary = atob(dataURI.split(',')[1]), array = [];
        for(var i = 0; i < binary.length; i++) array.push(binary.charCodeAt(i));
        return new Blob([new Uint8Array(array)], {type: dataTYPE});
    }

input dataURI is Data URL and dataTYPE is the file type and then output blob object

Waistcoat answered 26/7, 2013 at 8:10 Comment(2)
The dataTYPE is embedded in dataURI and hence should be parsed as in the first answer.Foretime
dataURL2Blob has come from my plugin for image process, you can check out this link. I just copy my own code. github.com/xenophon566/html5.upload/blob/master/js/…Waistcoat
L
12

XHR based method.

function dataURLtoBlob( dataUrl, callback )
{
    var req = new XMLHttpRequest;

    req.open( 'GET', dataUrl );
    req.responseType = 'arraybuffer'; // Can't use blob directly because of https://crbug.com/412752

    req.onload = function fileLoaded(e)
    {
        // If you require the blob to have correct mime type
        var mime = this.getResponseHeader('content-type');

        callback( new Blob([this.response], {type:mime}) );
    };

    req.send();
}

dataURLtoBlob( 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', function( blob )
{
    console.log( blob );
});
Lindell answered 5/6, 2015 at 12:8 Comment(3)
Seems, this is the best way now. Thank you!Salian
Will not work in Safari - it will throw cross-origin error if page is loaded from HTTPS.Experimentalize
The question is Blob from DataURL?Irreligious
T
5

try:

function dataURItoBlob(dataURI) {
    if(typeof dataURI !== 'string'){
        throw new Error('Invalid argument: dataURI must be a string');
    }
    dataURI = dataURI.split(',');
    var type = dataURI[0].split(':')[1].split(';')[0],
        byteString = atob(dataURI[1]),
        byteStringLength = byteString.length,
        arrayBuffer = new ArrayBuffer(byteStringLength),
        intArray = new Uint8Array(arrayBuffer);
    for (var i = 0; i < byteStringLength; i++) {
        intArray[i] = byteString.charCodeAt(i);
    }
    return new Blob([intArray], {
        type: type
    });
}
Tindle answered 5/1, 2015 at 14:13 Comment(0)
S
5

Since none of these answers support base64 and non-base64 dataURLs, here's one that does based on vuamitom's deleted answer:

// from https://mcmap.net/q/42033/-download-canvas-as-png-in-fabric-js-giving-network-error/
var dataURLtoBlob = exports.dataURLtoBlob = function(dataurl) {
    var parts = dataurl.split(','), mime = parts[0].match(/:(.*?);/)[1]
    if(parts[0].indexOf('base64') !== -1) {
        var bstr = atob(parts[1]), n = bstr.length, u8arr = new Uint8Array(n)
        while(n--){
            u8arr[n] = bstr.charCodeAt(n)
        }

        return new Blob([u8arr], {type:mime})
    } else {
        var raw = decodeURIComponent(parts[1])
        return new Blob([raw], {type: mime})
    }
}

Note: I'm not sure if there are other dataURL mime types that might have to be handled differently. But please let me know if you find out! Its possible that dataURLs can simply have any format they want, and in that case it'd be up to you to find the right code for your particular use case.

Synchronous answered 20/6, 2017 at 2:21 Comment(0)
C
2

Create a blob using XHR API:

function dataURLtoBlob( dataUrl, callback )
{
    var req = new XMLHttpRequest;

    req.open( 'GET', dataUrl );
    req.responseType = 'blob';

    req.onload = function fileLoaded(e)
    {
        callback(this.response);
    };

    req.send();
}

var dataURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='

dataURLtoBlob(dataURI , function( blob )
{
    console.log( blob );
});
Corona answered 15/3, 2019 at 21:41 Comment(1)
Chrome throws net::ERR_INVALID_URLBenco
D
1

Refactored version of the accepted answer.

// NOTE: This function doesn't handle URLEncoded Data URLs.
// Data URL format – data:[<mediatype>][;base64],<data>
function dataUrltoBlob(dataUrl: string): Blob {
  const [meta, data] = dataUrl.split(",")

  // Convert the base64 encoded data to a binary string.
  const byteString = atob(data)

  // Get the MIME type.
  const [mimeTypeWithDataPrefix] = meta.split(";")
  const mimeType = mimeTypeWithDataPrefix.replace("data:", "")

  // Convert the binary string to an ArrayBuffer.
  const arrayBuffer = Uint8Array.from(byteString, (c) => c.charCodeAt(0)).buffer

  // Create a blob from the ArrayBuffer.
  return new Blob([arrayBuffer], { type: mimeType })
}

P.S. Thanks to @devnull69 for the original solution.

Deadpan answered 8/12, 2023 at 12:31 Comment(0)
M
0

If you need something that works server-side on Google Apps Script, try:

function dataURItoBlob(dataURI) {
  // convert base64 to Byte[]
  // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
  var data = Utilities.base64Decode(dataURI.split(',')[1]);

  // separate out the mime component
  var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]

  var blob = Utilities.newBlob(data);
  blob.setContentType(mimeString);
  return blob;
}
Mutineer answered 1/1, 2022 at 17:43 Comment(0)
M
-2

use

FileReader.readAsArrayBuffer(Blob|File)

rather than

FileReader.readAsDataURL(Blob|File)
Materi answered 28/8, 2012 at 23:23 Comment(1)
I have to store it as a DataURL for an indefinite period in localStorage, so using the alternative ArrayBuffer path won't work.Frag

© 2022 - 2024 — McMap. All rights reserved.