31

I want to parse the requested image from my REST API into base64 string.

enter image description here

Firstly... I thought, it would be easy, just to use window.btoa() function for this aim.

When I try to do it in such part of my application:

.done( function( response, position ) {
    var texture = new Image();
    texture.src = "data:image/png;base64," + window.btoa( response ); 

I've got the next error: Uncaught InvalidCharacterError: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

As I read here: javascript atob returning 'String contains an invalid character'

The issue occurs because of newlines in the response and that's why window.btoa() failed. Any binary image format of course will have newlines... But as from the link above the suggestion was to remove/replace those characters - is a bad suggestion for me, because if to remove/replace some characters from binary image it just will be corrupted.

Of course, the possible alternatives relate to the API design: - to add some function, which return base64 representation - to add some function, which return url to the image

If I won't repair it, I shall return base64 representation from the server, but I don't like such a way.

Does exist some way to solve my problem with the handling binary image from response, as it's shown above in the part of screenshot, doesn't it?

11
  • What are you using API side? Commented Apr 11, 2014 at 13:26
  • @FabianCook Where does exactly? Where I have suggested to change API for returning base64/url or where? If you've read me carefully, I want to find a way not to use it and try to use some other way/trick. To make base64 response from the server-side - not a problem, but I've a desire to find so way to handle it as a binary image. Commented Apr 11, 2014 at 13:27
  • Ahh okay. Will look around. Hold up. Commented Apr 11, 2014 at 13:30
  • Would you be able to post the api call so I can test? Or do they have an example? I may have a solution Commented Apr 11, 2014 at 13:32
  • @FabianCook API call is simple as it could be: http://host/api/tile?x={x}&y={y}&zoom={zoom}, and then it returns just an image from the server side. Of course, I can add some option &base64={true | false} or /api/tile.{format}?{x}&y={y}&zoom={zoom}, where {format} = .base64, so it can be looked like: /api/tile.base?{x}&y={y}&zoom={zoom}, but it's another story, the question is about possible binary handling from response. Also the service is in localhost... Commented Apr 11, 2014 at 13:38

6 Answers 6

19

I think part of the problem you're hitting is that jQuery.ajax does not natively support XHR2 blob/arraybuffer types which can nicely handle binary data (see Reading binary files using jQuery.ajax).

If you use a native XHR object with xhr.responseType = 'arraybuffer', then read the response array and convert it to Base64, you'll get what you want.

Here's a solution that works for me:

// http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
function fetchBlob(uri, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', uri, true);
  xhr.responseType = 'arraybuffer';

  xhr.onload = function(e) {
    if (this.status == 200) {
      var blob = this.response;
      if (callback) {
        callback(blob);
      }
    }
  };
  xhr.send();
};

fetchBlob('https://i.imgur.com/uUGeiSFb.jpg', function(blob) {
  // Array buffer to Base64:
  var str = btoa(String.fromCharCode.apply(null, new Uint8Array(blob)));

  console.log(str);
  // Or: '<img src="data:image/jpeg;base64,' + str + '">'
});

https://jsfiddle.net/oy1pk8r3/2/

Produces base64 encoded console output: /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAIBAQIBAQICAgICAgICAw.....

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, it took me a while to realize that this solved my issue because of a typo, but this worked for me.
this saved my day
5

instead of looping through the blob with _arrayBufferToBase64(), use apply() to do the conversion, it's 30 times faster in my browser and is more concise

// http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
function fetchBlob(uri, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', uri, true);
  xhr.responseType = 'arraybuffer';

  xhr.onload = function(e) {
    if (this.status == 200) {
      var blob = this.response;
      if (callback) {
        callback(blob);
      }
    }
  };
  xhr.send();
};

fetchBlob('https://i.imgur.com/uUGeiSFb.jpg', function(blob) {
  var str = String.fromCharCode.apply(null, new Uint8Array(blob));
  console.log(str);
  // the base64 data: image is then
  // '<img src="data:image/jpeg;base64,' + btoa(str) + '" />'	
});

1 Comment

I've incorporated this into the above answer.
1

Im guessing to use escape on the string before you pass it to the function, without the API call I can't test myself.

test

encodeURI("testñ$☺PNW¢=")

returns

"test%C3%B1$%E2%98%BAPNW%C2%A2="

It just escapes all the characters, it should escape all the illegal characters in the string

test

encodeURI("¶!┼Æê‼_ðÄÄ┘Ì+\+,o▬MBc§yþó÷ö")

returns

"%C2%B6!%E2%94%BC%C3%86%C3%AA%E2%80%BC_%C3%B0%C3%84%C3%84%E2%94%98%C3%8C++,o%E2%96%ACMBc%C2%A7y%C3%BE%C3%B3%C3%B7%C3%B6"

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI

2 Comments

I'm afraid escaping will corrupt the image, so it can't be shown after some chars will be escaped, because some newlines and binary chars are required for the correct image show, but thanks for help and spending time for me.
I would suggest getting that feature request.
1

The issue you're encountering is that the response is being considered a Unicode String. See the section on Unicode Strings here: window.btoa

Several solutions are offered in this post

2 Comments

the response is a binary format, not a text one (so it can't be the unicode string as I think), if you look carefully, I've catched the binary content of PNG image
@GeloVolro - I did see that. I did quite a few searches when I got the same error using btoa. I'm not sure if Chrome displays objects with quotes around them, but considering it's displaying the data makes me think it is a string. Since it appears you're using jQuery, you could use the jQuery.type(response) to see the type.
-1

Try this on its working well. please try once. @user2402179

  $.ajax({
    type: 'GET',
    url: baseUrl",
    dataType: 'binary',
    xhr() {
      let myXhr = jQuery.ajaxSettings.xhr();
      myXhr.responseType = 'blob';
      return myXhr;
    },
    headers: {'Authorization': 'Bearer '+token}      
    
}).then((response) => {
    // response is a Blob
    return new Promise((resolve, reject) => {
      let reader = new FileReader();
      reader.addEventListener('load', () => {
        $('#theDiv').append('<img src="' +reader.result+ '" />')
        resolve(reader.result);
      }, false);
      reader.addEventListener('error', () => {
        reject(reader.error);
      }, false);
      reader.readAsDataURL(response);
    });
  });

Comments

-4

Base 64 Image data is worked for me like

<img src="data:image/png;base64,' + responce + '" />

2 Comments

Invalid characters should stop this. Thats what he is asking.
It's for the WebGL project, not just showing it as the static image, and again invalid chars...