16

For a web application I need to get my images with an ajax request because we have signature + authentication on our API, so we can't get images using a simple <img src="myapi/example/145"/>

Since we're using angular2, we obviously looked for blob or something like that, but as stated in static_response.d.ts file:

/**
 * Not yet implemented
 */
blob(): any;

So okay, I can't do it for now, I have to wait for thie feature to be implemented.

But problem is I can't wait so I need a hotfix or a little hack to be able to get image data from response and I'll be able to remove my hack and set the blob() method call to be good when it will be implemented.

I tried this:

export class AppComponent {
    constructor(private api:ApiService, private logger:Logger){}
    title = 'Tests api';
    src='http://placekitten.com/500/200'; //this is src attribute of my test image
    onClick(){ //Called when I click on "test" button
        this.api.test().then(res => {
            console.log(res._body);
            var blob = new Blob([new Uint8Array(res._body)],{
                type: res.headers.get("Content-Type")
            });
            var urlCreator = window.URL;
            this.src = urlCreator.createObjectURL(blob);
        });
    }
}

with ApiService.test() method:

test():Promise<any> {
        return this.http.get(this._baseUrl + "myapi/example/145", this.getOptions())
//getOptions() is just creating three custom headers for     
//authentication and CSRF protection using signature
            .toPromise()
            .then(res => {
                    this.logger.debug(res);
                if(res.headers.get("Content-Type").startsWith("image/")){
                    return res;
                }
                return res.json();
            })
            .catch(res => {
                this.logger.error(res);
                return res.json();
            } );
    }

But I don't get any image from it and logging the response data shows a big string which is image data.

Do you have a hack to achieve this?

Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
Supamiu
  • 8,501
  • 7
  • 42
  • 76

4 Answers4

27

It is not necessary to extend BrowserXhr anymore. (Tested with angular 2.2.1) RequestOptionsArgs now has a property responseType: ResponseContentType which can be set to ResponseContentType.Blob

Using DomSanitizer

import {DomSanitizer} from '@angular/platform-browser';

This example also creates a sanitized url that can be bound to the src property of an <img>

this.http.get(url,  {
                        headers: {'Content-Type': 'image/jpg'},
                        responseType: ResponseContentType.Blob
                    })
        .map(res => {
            return new Blob([res._body], {
                type: res.headers.get("Content-Type")
            });
        })
        .map(blob => {
            var urlCreator = window.URL;
            return  this.sanitizer.bypassSecurityTrustUrl(urlCreator.createObjectURL(blob));
        })
Demodave
  • 6,242
  • 6
  • 43
  • 58
tschuege
  • 761
  • 1
  • 8
  • 20
12

Using the new Angular HttpClient is really easy to achieve this. Going off of tschuege's approach, it would be:

return this._http.get('/api/images/' + _id, {responseType: 'blob'}).map(blob => {
  var urlCreator = window.URL;
  return this._sanitizer.bypassSecurityTrustUrl(urlCreator.createObjectURL(blob));
})

The key is to set the responseType as 'blob' so that it doesn't attempt to parse it as JSON

cuzox
  • 794
  • 7
  • 15
4

I think that you missed to set the responseType on your request. Right now it's a bit tricky because it's not supported.

The workaround would be to override the BrowserXhr class to set the responseType on the xhr object itself...

You could extend the BrowserXhr:

@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
  constructor() {}
  build(): any {
    let xhr = super.build();
    xhr.responseType = 'arraybuffer';
    return <any>(xhr);
  }
}

and override the BrowserXhr provider with the extended class:

bootstrap(AppComponent, [
  HTTP_PROVIDERS,
  provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);

The problem is here that you don't override for all requests. At the bootstrap level, it will override everything. So you could provide it in a sub injector within the providers attribute of the impacted component...

Here is a working plunkr: https://plnkr.co/edit/tC8xD16zwZ1UoEojebkm?p=preview.

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
-1

This JSFiddle could help you: https://jsfiddle.net/virginieLGB/yy7Zs/936/

The method is, as you wanted, creating a Blob from the URL provided

// Image returned should be an ArrayBuffer.
var xhr = new XMLHttpRequest();

xhr.open( "GET", "https://placekitten.com/500/200", true );

// Ask for the result as an ArrayBuffer.
xhr.responseType = "arraybuffer";

xhr.onload = function( e ) {
    // Obtain a blob: URL for the image data.
    var arrayBufferView = new Uint8Array( this.response );
    var blob = new Blob( [ arrayBufferView ], { type: "image/jpeg" } );
    var urlCreator = window.URL || window.webkitURL;
    var imageUrl = urlCreator.createObjectURL( blob );
    var img = document.querySelector( "#photo" );
    img.src = imageUrl;
};

xhr.send();
VirginieLGB
  • 548
  • 2
  • 15
  • Problem is that this is a full JS solution, I need a TS hack which can be achieved with Response object to be easy to remove when blob() function will be available. – Supamiu Mar 22 '16 at 12:44