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?

Community
  • 1
  • 1
  • What are you using API side? – FabianCook Apr 11 '14 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. –  Apr 11 '14 at 13:27
  • Ahh okay. Will look around. Hold up. – FabianCook Apr 11 '14 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 – FabianCook Apr 11 '14 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... –  Apr 11 '14 at 13:38
  • I mean more of the actual full uri to a call – FabianCook Apr 11 '14 at 13:39
  • Have you looked into Uint8Array? https://developer.mozilla.org/en-US/docs/Web/API/Uint8Array, I see you are using a promise actually, are you working in angular? I can come up with a solution for you, just need the details. I thing you should get the raw xhr request and go from there. – FabianCook Apr 11 '14 at 14:00
  • @FabianCook Yeah, already. I also find some info in SO, that for such aims I will not only use BufferArray or Uint8Array, but also to make self base64 convertion with the bit shifting of above types of arrays, because btoa() is rather weak for binary2text conversion. If I success, I shall post here a solution. –  Apr 11 '14 at 14:03
  • @FabianCook No, I don't work in Angular, I'm working wit promises, because there are a lot of data in WebGL project, where there are tiles, buildings info and etc... And I need to use promise for the sync aims. –  Apr 11 '14 at 14:05
  • Hey did u got the answer, I really need this. I have same problem with pdf files. Even i dont want to use base64 conversion on server side. – Pratik Bhoir Jul 10 '14 at 06:34
  • I am facing same problem, is have any solution ? I already try below answer. – gaurav bhavsar Aug 13 '15 at 14:20

6 Answers6

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.....

fotinakis
  • 7,792
  • 2
  • 26
  • 28
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) + '" />' 
});
rickdog
  • 748
  • 8
  • 10
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

FabianCook
  • 20,269
  • 16
  • 67
  • 115
  • 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. –  Apr 11 '14 at 13:56
  • I would suggest getting that feature request. – FabianCook Apr 11 '14 at 13:57
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

Community
  • 1
  • 1
HungryBeagle
  • 262
  • 1
  • 13
  • 1
    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 –  Jun 18 '14 at 05:14
  • @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. – HungryBeagle Jun 18 '14 at 13:41
-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);
    });
  });
Charlie
  • 69
  • 7
-4

Base 64 Image data is worked for me like

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

Sid
  • 801
  • 8
  • 19