1

A rest webservice I'm querying prompts the download modal window when I send a request. This is the response header

{
  "access-control-allow-credentials": "true",
  "access-control-allow-headers": "x-requested-with, content-type, authorization",
  "access-control-allow-methods": "GET, POST, OPTIONS",
  "access-control-allow-origin": "*",
  "connection": "close",
  "content-disposition": "attachment; filename =AR2.png",
  "content-type": "application/octet-stream",
  "date": "Tue, 28 Apr 2020 17:17:18 GMT",
  "server": "Apache-Coyote/1.1",
  "transfer-encoding": "chunked"
}

so if I try to fetch and print the response body in console I get an unreadable text starting with \89PNG, of course. Is there a way to render the PNG in a html page? I tried Blob and FileReader, but I'm not sure how to use them for this use case.

An attempt I've done

var response = // XHR response
var blob = new Blob([response]);
var imageUrl = urlCreator.createObjectURL(blob);
document.querySelector("#image").src = imageUrl;

The html page just shows the icon of a broken src where the image should be.

I tried to print the whole event (xhr.onload callback). responseType seems empty. enter image description here

alfredopacino
  • 2,979
  • 9
  • 42
  • 68
  • 1
    If you're using `fetch` you can get the blob and create a temporary URL to access it in an ` – rickdenhaan Apr 28 '20 at 17:34
  • Actually I use XMLHttpRequest. I tried that version and I get the error `Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided. at XMLHttpRequest.request.onload`. `createObjectURL` seems to be a static method, so I tried `URL.createObjectURL`, same error. – alfredopacino Apr 28 '20 at 18:05
  • Can you add the code with your current attempt? It should be `URL.createObjectURL(theBlobYouHave)` -- that error usually means that whatever you're passing to it is not a Blob or File object. – rickdenhaan Apr 28 '20 at 19:30
  • Posting the whole code is tricky because the remote request is done by another library. Anyway that's not the point, the library doesn't do any manipulation of the response in case of `conten-type: application/octet-stream` – alfredopacino Apr 29 '20 at 11:25
  • what is the xhr.responseType. it should be blob – user120242 Apr 29 '20 at 11:33
  • see edited question. `responseType` seems empty – alfredopacino Apr 29 '20 at 11:42
  • set `xhr.responseType = 'blob'` before you make the request – user120242 Apr 29 '20 at 11:43
  • I'm afraid that would introduce bugs somewhere else, is there no way to convert whatever that thing is in Blob? – alfredopacino Apr 29 '20 at 11:46
  • `new Blob([response])` should work – user120242 Apr 29 '20 at 11:48
  • see edited question, I replaced the code of my previous attempt. – alfredopacino Apr 29 '20 at 11:55
  • at minimum you must have: `xhr.overrideMimeType('text/plain; charset=x-user-defined')` – user120242 Apr 29 '20 at 12:25

1 Answers1

2

At minimum you must call this, or you are not getting the binary data. Without it your request is getting text data only (character set is limited), so it is impossible because of the loss of data.
You have to either change the server-side to return base64 or change the way your XHR request is being made so that you get the binary data:

        xhr.overrideMimeType('text/plain; charset=x-user-defined');

Going from binary string to blob:

var xhr = new XMLHttpRequest();
    xhr.open('get','https://i.imgur.com/2T4Vd.png')
  xhr.onload = function(){
   var img = new Image();
   var response = xhr.responseText;
   var binary = new Uint8Array(response.length);
   
   for(var i=0;i<response.length;i++){
    binary[i] = response.charCodeAt(i) & 0xff;
   }
   
   img.src = URL.createObjectURL(new Blob([binary.buffer]));
            document.body.appendChild(img)

  }
  xhr.overrideMimeType('text/plain; charset=x-user-defined');
  xhr.send();

Converting to base64

var binary="";
for(var i=0; i<response.length ; i++){
    binary += String.fromCharCode(response.charCodeAt(i) & 0xff);
}

img.src = 'data:image/png;base64,' + btoa(binary);

If you are getting an ArrayBuffer, convert ArrayBuffer to Blob:

var blob = new Blob([response]);

var image = URL.createObjectURL( blob );

You should be setting the responseType on the XHR request, so that you can get a blob directly.

xhr.responseType = 'blob'

You mention the use of fetch. The response object on fetch includes .blob() which you would be calling to get a blob object.

const myBlob = await (await fetch('/url')).blob();
user120242
  • 14,918
  • 3
  • 38
  • 52
  • As I said in the other comment, I would prefer not changing the library in charge of doing remote request for that, it would introduce bugs in case of other `application/octet-stream` responses. Is there a way to convert in `Blob` the response itself? – alfredopacino Apr 29 '20 at 11:48
  • If it's still not working you are getting a `string` result, which will not work. – user120242 Apr 29 '20 at 11:55
  • To clarify, you are getting a text string with a limited character set and thus there is a loss of data in the response string. – user120242 Apr 29 '20 at 12:45
  • @alfredopacino in fact, your remote request code shouldn't be working with other `application/octet-stream` responses unless they are also at their base really just purely text files. There must be a parameter or hook somewhere that allows for requesting binary data. – user120242 Apr 29 '20 at 12:54